Merge branch 'x86-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 28 Dec 2008 20:07:57 +0000 (12:07 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 28 Dec 2008 20:07:57 +0000 (12:07 -0800)
* 'x86-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (246 commits)
  x86: traps.c replace #if CONFIG_X86_32 with #ifdef CONFIG_X86_32
  x86: PAT: fix address types in track_pfn_vma_new()
  x86: prioritize the FPU traps for the error code
  x86: PAT: pfnmap documentation update changes
  x86: PAT: move track untrack pfnmap stubs to asm-generic
  x86: PAT: remove follow_pfnmap_pte in favor of follow_phys
  x86: PAT: modify follow_phys to return phys_addr prot and return value
  x86: PAT: clarify is_linear_pfn_mapping() interface
  x86: ia32_signal: remove unnecessary declaration
  x86: common.c boot_cpu_stack and boot_exception_stacks should be static
  x86: fix intel x86_64 llc_shared_map/cpu_llc_id anomolies
  x86: fix warning in arch/x86/kernel/microcode_amd.c
  x86: ia32.h: remove unused struct sigfram32 and rt_sigframe32
  x86: asm-offset_64: use rt_sigframe_ia32
  x86: sigframe.h: include headers for dependency
  x86: traps.c declare functions before they get used
  x86: PAT: update documentation to cover pgprot and remap_pfn related changes - v3
  x86: PAT: add pgprot_writecombine() interface for drivers - v3
  x86: PAT: change pgprot_noncached to uc_minus instead of strong uc - v3
  x86: PAT: implement track/untrack of pfnmap regions for x86 - v3
  ...

697 files changed:
Documentation/cpu-freq/user-guide.txt
Documentation/credentials.txt [new file with mode: 0644]
Documentation/kernel-parameters.txt
Documentation/scheduler/sched-design-CFS.txt
Documentation/sh/kgdb.txt [deleted file]
Documentation/sound/alsa/ALSA-Configuration.txt
Documentation/sound/alsa/HD-Audio-Models.txt [new file with mode: 0644]
Documentation/sound/alsa/HD-Audio.txt [new file with mode: 0644]
Documentation/sound/alsa/Procfile.txt
Documentation/sound/alsa/soc/machine.txt
MAINTAINERS
arch/alpha/kernel/asm-offsets.c
arch/alpha/kernel/entry.S
arch/arm/mach-pxa/include/mach/palmasoc.h [new file with mode: 0644]
arch/ia64/ia32/sys_ia32.c
arch/ia64/kernel/mca_drv.c
arch/ia64/kernel/perfmon.c
arch/ia64/kernel/signal.c
arch/mips/kernel/kspd.c
arch/mips/kernel/mips-mt-fpaff.c
arch/mips/kernel/vpe.c
arch/parisc/kernel/signal.c
arch/powerpc/mm/fault.c
arch/powerpc/platforms/cell/spufs/inode.c
arch/s390/hypfs/inode.c
arch/s390/kernel/compat_linux.c
arch/sh/Kconfig
arch/sh/Kconfig.debug
arch/sh/Makefile
arch/sh/boards/Kconfig
arch/sh/boards/Makefile
arch/sh/boards/board-ap325rxa.c
arch/sh/boards/board-shmin.c
arch/sh/boards/mach-cayman/Makefile
arch/sh/boards/mach-cayman/irq.c
arch/sh/boards/mach-cayman/led.c [deleted file]
arch/sh/boards/mach-dreamcast/irq.c
arch/sh/boards/mach-dreamcast/setup.c
arch/sh/boards/mach-edosk7705/Makefile
arch/sh/boards/mach-edosk7705/io.c
arch/sh/boards/mach-edosk7705/setup.c
arch/sh/boards/mach-hp6xx/pm.c
arch/sh/boards/mach-microdev/Makefile
arch/sh/boards/mach-microdev/fdc37c93xapm.c [new file with mode: 0644]
arch/sh/boards/mach-microdev/irq.c
arch/sh/boards/mach-microdev/led.c [deleted file]
arch/sh/boards/mach-microdev/setup.c
arch/sh/boards/mach-migor/setup.c
arch/sh/boards/mach-rsk/Kconfig [new file with mode: 0644]
arch/sh/boards/mach-rsk/Makefile [new file with mode: 0644]
arch/sh/boards/mach-rsk/devices-rsk7203.c [moved from arch/sh/boards/board-rsk7203.c with 58% similarity]
arch/sh/boards/mach-rsk/setup.c [new file with mode: 0644]
arch/sh/boards/mach-se/7343/Makefile
arch/sh/boards/mach-se/7343/io.c [deleted file]
arch/sh/boards/mach-se/7343/setup.c
arch/sh/boards/mach-se/770x/setup.c
arch/sh/boards/mach-se/7721/setup.c
arch/sh/boards/mach-se/7722/setup.c
arch/sh/boards/mach-sh03/setup.c
arch/sh/boards/mach-systemh/irq.c
arch/sh/cchips/hd6446x/hd64461.c
arch/sh/configs/edosk7705_defconfig [new file with mode: 0644]
arch/sh/configs/rsk7201_defconfig [new file with mode: 0644]
arch/sh/configs/rsk7203_defconfig
arch/sh/configs/rts7751r2dplus_qemu_defconfig [deleted file]
arch/sh/configs/se7343_defconfig
arch/sh/drivers/dma/Makefile
arch/sh/drivers/dma/dma-isa.c [deleted file]
arch/sh/drivers/dma/dma-sh.c
arch/sh/drivers/dma/dma-sh.h
arch/sh/drivers/pci/ops-sh03.c
arch/sh/drivers/pci/pci-sh7780.c
arch/sh/include/asm/addrspace.h
arch/sh/include/asm/bitops-grb.h
arch/sh/include/asm/bitops-irq.h [deleted file]
arch/sh/include/asm/bitops-llsc.h
arch/sh/include/asm/bitops-op32.h [new file with mode: 0644]
arch/sh/include/asm/bitops.h
arch/sh/include/asm/bugs.h
arch/sh/include/asm/elf.h
arch/sh/include/asm/ftrace.h
arch/sh/include/asm/io.h
arch/sh/include/asm/kgdb.h
arch/sh/include/asm/machvec.h
arch/sh/include/asm/mmu_context.h
arch/sh/include/asm/mutex-llsc.h [new file with mode: 0644]
arch/sh/include/asm/mutex.h
arch/sh/include/asm/pm.h [deleted file]
arch/sh/include/asm/processor.h
arch/sh/include/asm/processor_32.h
arch/sh/include/asm/processor_64.h
arch/sh/include/asm/ptrace.h
arch/sh/include/asm/sh_bios.h
arch/sh/include/asm/string_64.h
arch/sh/include/asm/syscall_32.h
arch/sh/include/asm/syscall_64.h
arch/sh/include/asm/system.h
arch/sh/include/asm/unaligned-sh4a.h [new file with mode: 0644]
arch/sh/include/asm/unaligned.h
arch/sh/include/cpu-sh3/cpu/gpio.h
arch/sh/include/mach-common/mach/edosk7705.h
arch/sh/include/mach-se/mach/mrshpc.h [new file with mode: 0644]
arch/sh/include/mach-se/mach/se.h
arch/sh/include/mach-se/mach/se7343.h
arch/sh/kernel/Makefile_32
arch/sh/kernel/Makefile_64
arch/sh/kernel/cf-enabler.c [deleted file]
arch/sh/kernel/cpu/clock.c
arch/sh/kernel/cpu/init.c
arch/sh/kernel/cpu/sh2a/Makefile
arch/sh/kernel/cpu/sh2a/clock-sh7201.c [new file with mode: 0644]
arch/sh/kernel/cpu/sh2a/probe.c
arch/sh/kernel/cpu/sh2a/setup-sh7201.c [new file with mode: 0644]
arch/sh/kernel/cpu/sh3/entry.S
arch/sh/kernel/cpu/sh3/ex.S
arch/sh/kernel/cpu/sh4/softfloat.c
arch/sh/kernel/cpu/sh4a/clock-sh7722.c
arch/sh/kernel/cpu/sh4a/setup-sh7343.c
arch/sh/kernel/cpu/sh4a/setup-sh7366.c
arch/sh/kernel/cpu/sh4a/setup-sh7722.c
arch/sh/kernel/cpu/sh4a/setup-sh7723.c
arch/sh/kernel/debugtraps.S
arch/sh/kernel/disassemble.c [new file with mode: 0644]
arch/sh/kernel/entry-common.S
arch/sh/kernel/ftrace.c [new file with mode: 0644]
arch/sh/kernel/head_32.S
arch/sh/kernel/idle.c [new file with mode: 0644]
arch/sh/kernel/kgdb.c [new file with mode: 0644]
arch/sh/kernel/kgdb_jmp.S [deleted file]
arch/sh/kernel/kgdb_stub.c [deleted file]
arch/sh/kernel/pm.c [deleted file]
arch/sh/kernel/process_32.c
arch/sh/kernel/process_64.c
arch/sh/kernel/ptrace_64.c
arch/sh/kernel/setup.c
arch/sh/kernel/sh_bios.c
arch/sh/kernel/sh_ksyms_32.c
arch/sh/kernel/sh_ksyms_64.c
arch/sh/kernel/signal_32.c
arch/sh/kernel/signal_64.c
arch/sh/kernel/sys_sh.c
arch/sh/kernel/time_32.c
arch/sh/kernel/time_64.c
arch/sh/kernel/timers/timer-mtu2.c
arch/sh/kernel/traps_32.c
arch/sh/lib/Makefile
arch/sh/lib/ashiftrt.S [new file with mode: 0644]
arch/sh/lib/ashldi3.c [new file with mode: 0644]
arch/sh/lib/ashlsi3.S [new file with mode: 0644]
arch/sh/lib/ashrdi3.c [new file with mode: 0644]
arch/sh/lib/ashrsi3.S [new file with mode: 0644]
arch/sh/lib/libgcc.h [new file with mode: 0644]
arch/sh/lib/lshrdi3.c [new file with mode: 0644]
arch/sh/lib/lshrsi3.S [new file with mode: 0644]
arch/sh/lib/mcount.S [new file with mode: 0644]
arch/sh/lib/movmem.S [new file with mode: 0644]
arch/sh/lib/udiv_qrnnd.S [new file with mode: 0644]
arch/sh/lib/udivsi3.S [new file with mode: 0644]
arch/sh/lib/udivsi3_i4i-Os.S [new file with mode: 0644]
arch/sh/lib/udivsi3_i4i.S [new file with mode: 0644]
arch/sh/lib64/Makefile
arch/sh/lib64/c-checksum.c
arch/sh/lib64/memcpy.S [new file with mode: 0644]
arch/sh/lib64/memcpy.c [deleted file]
arch/sh/lib64/memset.S [new file with mode: 0644]
arch/sh/lib64/sdivsi3.S [new file with mode: 0644]
arch/sh/lib64/strcpy.S [new file with mode: 0644]
arch/sh/lib64/strlen.S [new file with mode: 0644]
arch/sh/lib64/udivdi3.S [new file with mode: 0644]
arch/sh/lib64/udivsi3.S [new file with mode: 0644]
arch/sh/mm/Makefile_32
arch/sh/mm/Makefile_64
arch/sh/mm/asids-debugfs.c [new file with mode: 0644]
arch/sh/mm/consistent.c
arch/sh/mm/fault_32.c
arch/sh/mm/ioremap_32.c
arch/sh/mm/mmap.c
arch/sh/oprofile/Makefile
arch/sh/oprofile/backtrace.c [new file with mode: 0644]
arch/sh/oprofile/common.c [new file with mode: 0644]
arch/sh/oprofile/op_impl.h [new file with mode: 0644]
arch/sh/oprofile/op_model_null.c [deleted file]
arch/sh/oprofile/op_model_sh7750.c
arch/sh/tools/mach-types
arch/um/drivers/mconsole_kern.c
arch/x86/crypto/crc32c-intel.c
arch/x86/ia32/ia32_aout.c
arch/x86/mm/fault.c
crypto/Kconfig
crypto/Makefile
crypto/aes_generic.c
crypto/ahash.c
crypto/ansi_cprng.c
crypto/api.c
crypto/authenc.c
crypto/camellia.c
crypto/crc32c.c
crypto/crypto_null.c
crypto/des_generic.c
crypto/fcrypt.c
crypto/hmac.c
crypto/internal.h
crypto/md4.c
crypto/md5.c
crypto/michael_mic.c
crypto/proc.c
crypto/rmd128.c
crypto/rmd160.c
crypto/rmd256.c
crypto/rmd320.c
crypto/salsa20_generic.c
crypto/sha1_generic.c
crypto/sha256_generic.c
crypto/sha512_generic.c
crypto/shash.c [new file with mode: 0644]
crypto/testmgr.c
crypto/testmgr.h
crypto/tgr192.c
crypto/wp512.c
drivers/block/loop.c
drivers/char/tty_audit.c
drivers/char/tty_io.c
drivers/connector/cn_proc.c
drivers/crypto/hifn_795x.c
drivers/crypto/padlock-aes.c
drivers/crypto/talitos.c
drivers/crypto/talitos.h
drivers/i2c/busses/i2c-sh_mobile.c
drivers/input/keyboard/sh_keysc.c
drivers/isdn/capi/capifs.c
drivers/isdn/hysdn/hysdn_procconf.c
drivers/media/video/Kconfig
drivers/media/video/sh_mobile_ceu_camera.c
drivers/net/tun.c
drivers/serial/sh-sci.c
drivers/serial/sh-sci.h
drivers/usb/core/devio.c
drivers/usb/core/inode.c
drivers/usb/gadget/m66592-udc.c
drivers/usb/gadget/m66592-udc.h
drivers/usb/host/r8a66597-hcd.c
drivers/usb/host/r8a66597.h
drivers/video/Kconfig
drivers/video/fb_defio.c
drivers/video/sh7760fb.c
drivers/video/sh_mobile_lcdcfb.c
fs/9p/fid.c
fs/9p/vfs_inode.c
fs/9p/vfs_super.c
fs/affs/inode.c
fs/affs/super.c
fs/anon_inodes.c
fs/attr.c
fs/autofs/inode.c
fs/autofs4/dev-ioctl.c
fs/autofs4/inode.c
fs/autofs4/waitq.c
fs/bfs/dir.c
fs/binfmt_aout.c
fs/binfmt_elf.c
fs/binfmt_elf_fdpic.c
fs/binfmt_flat.c
fs/binfmt_som.c
fs/cifs/cifs_fs_sb.h
fs/cifs/cifsproto.h
fs/cifs/connect.c
fs/cifs/dir.c
fs/cifs/inode.c
fs/cifs/ioctl.c
fs/cifs/misc.c
fs/coda/cache.c
fs/coda/file.c
fs/coda/upcall.c
fs/compat.c
fs/devpts/inode.c
fs/dquot.c
fs/ecryptfs/ecryptfs_kernel.h
fs/ecryptfs/kthread.c
fs/ecryptfs/main.c
fs/ecryptfs/messaging.c
fs/ecryptfs/miscdev.c
fs/exec.c
fs/exportfs/expfs.c
fs/ext2/balloc.c
fs/ext2/ialloc.c
fs/ext3/balloc.c
fs/ext3/ialloc.c
fs/ext4/balloc.c
fs/ext4/ialloc.c
fs/fat/file.c
fs/fat/inode.c
fs/fcntl.c
fs/file_table.c
fs/fuse/dev.c
fs/fuse/dir.c
fs/gfs2/inode.c
fs/hfs/inode.c
fs/hfs/super.c
fs/hfsplus/inode.c
fs/hfsplus/options.c
fs/hpfs/namei.c
fs/hpfs/super.c
fs/hppfs/hppfs.c
fs/hugetlbfs/inode.c
fs/inotify_user.c
fs/internal.h
fs/ioprio.c
fs/jfs/jfs_inode.c
fs/locks.c
fs/minix/bitmap.c
fs/namei.c
fs/namespace.c
fs/ncpfs/ioctl.c
fs/nfsctl.c
fs/nfsd/auth.c
fs/nfsd/nfs4recover.c
fs/nfsd/nfsfh.c
fs/nfsd/vfs.c
fs/ocfs2/dlm/dlmfs.c
fs/ocfs2/namei.c
fs/omfs/inode.c
fs/open.c
fs/pipe.c
fs/posix_acl.c
fs/proc/array.c
fs/proc/base.c
fs/quota.c
fs/ramfs/inode.c
fs/reiserfs/namei.c
fs/smbfs/dir.c
fs/smbfs/inode.c
fs/smbfs/proc.c
fs/super.c
fs/sysv/ialloc.c
fs/ubifs/budget.c
fs/ubifs/dir.c
fs/udf/ialloc.c
fs/udf/namei.c
fs/ufs/ialloc.c
fs/xfs/linux-2.6/xfs_cred.h
fs/xfs/linux-2.6/xfs_globals.h
fs/xfs/linux-2.6/xfs_ioctl.c
fs/xfs/xfs_acl.c
fs/xfs/xfs_inode.h
fs/xfs/xfs_vnodeops.h
include/crypto/aes.h
include/crypto/algapi.h
include/crypto/hash.h
include/crypto/internal/hash.h
include/keys/keyring-type.h [new file with mode: 0644]
include/linux/audit.h
include/linux/binfmts.h
include/linux/capability.h
include/linux/crc32c.h
include/linux/cred.h
include/linux/crypto.h
include/linux/fb.h
include/linux/fs.h
include/linux/init_task.h
include/linux/input.h
include/linux/key-ui.h [deleted file]
include/linux/key.h
include/linux/keyctl.h
include/linux/mfd/wm8350/audio.h
include/linux/nsproxy.h
include/linux/sched.h
include/linux/securebits.h
include/linux/security.h
include/linux/tty.h
include/linux/user_namespace.h
include/net/scm.h
include/sound/ac97_codec.h
include/sound/asound.h
include/sound/core.h
include/sound/info.h
include/sound/jack.h
include/sound/l3.h [new file with mode: 0644]
include/sound/s3c24xx_uda134x.h [new file with mode: 0644]
include/sound/soc-dai.h [new file with mode: 0644]
include/sound/soc-dapm.h
include/sound/soc.h
include/sound/uda134x.h [new file with mode: 0644]
include/sound/version.h
include/video/sh_mobile_lcdc.h
init/main.c
ipc/mqueue.c
ipc/shm.c
ipc/util.c
kernel/Makefile
kernel/acct.c
kernel/auditsc.c
kernel/capability.c
kernel/cgroup.c
kernel/cred-internals.h [new file with mode: 0644]
kernel/cred.c [new file with mode: 0644]
kernel/exit.c
kernel/fork.c
kernel/futex.c
kernel/futex_compat.c
kernel/kmod.c
kernel/nsproxy.c
kernel/ptrace.c
kernel/sched.c
kernel/signal.c
kernel/sys.c
kernel/sysctl.c
kernel/timer.c
kernel/trace/trace.c
kernel/tsacct.c
kernel/uid16.c
kernel/user.c
kernel/user_namespace.c
kernel/workqueue.c
lib/Kconfig
lib/Makefile
lib/is_single_threaded.c [new file with mode: 0644]
lib/libcrc32c.c
mm/mempolicy.c
mm/migrate.c
mm/oom_kill.c
mm/shmem.c
net/9p/client.c
net/ax25/af_ax25.c
net/ax25/ax25_route.c
net/core/dev.c
net/core/scm.c
net/ipv4/netfilter/ipt_LOG.c
net/ipv6/ip6_flowlabel.c
net/ipv6/netfilter/ip6t_LOG.c
net/netfilter/nfnetlink_log.c
net/netfilter/xt_owner.c
net/netrom/af_netrom.c
net/rose/af_rose.c
net/rxrpc/ar-key.c
net/sched/cls_flow.c
net/socket.c
net/sunrpc/auth.c
net/unix/af_unix.c
scripts/recordmcount.pl
security/capability.c
security/commoncap.c
security/keys/internal.h
security/keys/key.c
security/keys/keyctl.c
security/keys/keyring.c
security/keys/permission.c
security/keys/proc.c
security/keys/process_keys.c
security/keys/request_key.c
security/keys/request_key_auth.c
security/root_plug.c
security/security.c
security/selinux/exports.c
security/selinux/hooks.c
security/selinux/include/av_perm_to_string.h
security/selinux/include/av_permissions.h
security/selinux/include/class_to_string.h
security/selinux/include/flask.h
security/selinux/include/objsec.h
security/selinux/nlmsgtab.c
security/selinux/selinuxfs.c
security/selinux/xfrm.c
security/smack/smack_access.c
security/smack/smack_lsm.c
security/smack/smackfs.c
sound/ac97_bus.c
sound/aoa/codecs/Makefile
sound/aoa/codecs/onyx.c [moved from sound/aoa/codecs/snd-aoa-codec-onyx.c with 99% similarity]
sound/aoa/codecs/onyx.h [moved from sound/aoa/codecs/snd-aoa-codec-onyx.h with 100% similarity]
sound/aoa/codecs/tas-basstreble.h [moved from sound/aoa/codecs/snd-aoa-codec-tas-basstreble.h with 100% similarity]
sound/aoa/codecs/tas-gain-table.h [moved from sound/aoa/codecs/snd-aoa-codec-tas-gain-table.h with 100% similarity]
sound/aoa/codecs/tas.c [moved from sound/aoa/codecs/snd-aoa-codec-tas.c with 99% similarity]
sound/aoa/codecs/tas.h [moved from sound/aoa/codecs/snd-aoa-codec-tas.h with 100% similarity]
sound/aoa/codecs/toonie.c [moved from sound/aoa/codecs/snd-aoa-codec-toonie.c with 98% similarity]
sound/aoa/core/Makefile
sound/aoa/core/alsa.c [moved from sound/aoa/core/snd-aoa-alsa.c with 98% similarity]
sound/aoa/core/alsa.h [moved from sound/aoa/core/snd-aoa-alsa.h with 100% similarity]
sound/aoa/core/core.c [moved from sound/aoa/core/snd-aoa-core.c with 99% similarity]
sound/aoa/core/gpio-feature.c [moved from sound/aoa/core/snd-aoa-gpio-feature.c with 99% similarity]
sound/aoa/core/gpio-pmf.c [moved from sound/aoa/core/snd-aoa-gpio-pmf.c with 100% similarity]
sound/aoa/fabrics/Makefile
sound/aoa/fabrics/layout.c [moved from sound/aoa/fabrics/snd-aoa-fabric-layout.c with 99% similarity]
sound/aoa/soundbus/i2sbus/Makefile
sound/aoa/soundbus/i2sbus/control.c [moved from sound/aoa/soundbus/i2sbus/i2sbus-control.c with 100% similarity]
sound/aoa/soundbus/i2sbus/core.c [moved from sound/aoa/soundbus/i2sbus/i2sbus-core.c with 99% similarity]
sound/aoa/soundbus/i2sbus/i2sbus.h
sound/aoa/soundbus/i2sbus/interface.h [moved from sound/aoa/soundbus/i2sbus/i2sbus-interface.h with 100% similarity]
sound/aoa/soundbus/i2sbus/pcm.c [moved from sound/aoa/soundbus/i2sbus/i2sbus-pcm.c with 100% similarity]
sound/core/Kconfig
sound/core/Makefile
sound/core/device.c
sound/core/hrtimer.c [new file with mode: 0644]
sound/core/info.c
sound/core/init.c
sound/core/jack.c
sound/core/rawmidi.c
sound/core/rtctimer.c
sound/core/seq/seq.c
sound/core/timer.c
sound/drivers/Kconfig
sound/drivers/pcsp/pcsp.c
sound/drivers/pcsp/pcsp.h
sound/drivers/pcsp/pcsp_lib.c
sound/drivers/vx/vx_core.c
sound/drivers/vx/vx_pcm.c
sound/isa/sb/sb8.c
sound/pci/Kconfig
sound/pci/ac97/ac97_codec.c
sound/pci/ac97/ac97_patch.c
sound/pci/ca0106/ca0106.h
sound/pci/ca0106/ca0106_main.c
sound/pci/ca0106/ca0106_mixer.c
sound/pci/cs46xx/cs46xx_lib.c
sound/pci/cs5535audio/Makefile
sound/pci/cs5535audio/cs5535audio.c
sound/pci/cs5535audio/cs5535audio.h
sound/pci/cs5535audio/cs5535audio_olpc.c [new file with mode: 0644]
sound/pci/cs5535audio/cs5535audio_pcm.c
sound/pci/emu10k1/emu10k1_main.c
sound/pci/emu10k1/emumixer.c
sound/pci/es1968.c
sound/pci/hda/Kconfig [new file with mode: 0644]
sound/pci/hda/Makefile
sound/pci/hda/hda_beep.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_eld.c [new file with mode: 0644]
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_hwdep.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_local.h
sound/pci/hda/hda_patch.h [deleted file]
sound/pci/hda/hda_proc.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_atihdmi.c
sound/pci/hda/patch_cmedia.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_intelhdmi.c [new file with mode: 0644]
sound/pci/hda/patch_nvhdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_si3054.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c
sound/pci/ice1712/ice1724.c
sound/pci/maestro3.c
sound/pci/mixart/mixart.c
sound/pci/mixart/mixart_core.c
sound/pci/oxygen/oxygen.c
sound/pci/pcxhr/Makefile
sound/pci/pcxhr/pcxhr.c
sound/pci/pcxhr/pcxhr.h
sound/pci/pcxhr/pcxhr_core.c
sound/pci/pcxhr/pcxhr_core.h
sound/pci/pcxhr/pcxhr_hwdep.c
sound/pci/pcxhr/pcxhr_mix22.c [new file with mode: 0644]
sound/pci/pcxhr/pcxhr_mix22.h [new file with mode: 0644]
sound/pci/pcxhr/pcxhr_mixer.c
sound/pci/riptide/riptide.c
sound/pci/rme9652/hdsp.c
sound/pci/rme9652/hdspm.c
sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
sound/ppc/pmac.c
sound/ppc/tumbler.c
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/at32/Kconfig [deleted file]
sound/soc/at32/Makefile [deleted file]
sound/soc/at32/at32-pcm.c [deleted file]
sound/soc/at32/at32-pcm.h [deleted file]
sound/soc/at32/at32-ssc.c [deleted file]
sound/soc/at32/at32-ssc.h [deleted file]
sound/soc/at91/Kconfig [deleted file]
sound/soc/at91/Makefile [deleted file]
sound/soc/at91/at91-pcm.c [deleted file]
sound/soc/at91/at91-pcm.h [deleted file]
sound/soc/at91/at91-ssc.c [deleted file]
sound/soc/at91/at91-ssc.h [deleted file]
sound/soc/atmel/Kconfig [new file with mode: 0644]
sound/soc/atmel/Makefile [new file with mode: 0644]
sound/soc/atmel/atmel-pcm.c [new file with mode: 0644]
sound/soc/atmel/atmel-pcm.h [new file with mode: 0644]
sound/soc/atmel/atmel_ssc_dai.c [new file with mode: 0644]
sound/soc/atmel/atmel_ssc_dai.h [new file with mode: 0644]
sound/soc/atmel/playpaq_wm8510.c [moved from sound/soc/at32/playpaq_wm8510.c with 98% similarity]
sound/soc/atmel/sam9g20_wm8731.c [new file with mode: 0644]
sound/soc/au1x/dbdma2.c
sound/soc/au1x/psc-ac97.c
sound/soc/au1x/psc-i2s.c
sound/soc/au1x/sample-ac97.c
sound/soc/blackfin/Kconfig
sound/soc/blackfin/bf5xx-ac97-pcm.c
sound/soc/blackfin/bf5xx-ac97.c
sound/soc/blackfin/bf5xx-ac97.h
sound/soc/blackfin/bf5xx-ad1980.c
sound/soc/blackfin/bf5xx-ad73311.c
sound/soc/blackfin/bf5xx-i2s-pcm.c
sound/soc/blackfin/bf5xx-i2s.c
sound/soc/blackfin/bf5xx-sport.h
sound/soc/blackfin/bf5xx-ssm2602.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ac97.c
sound/soc/codecs/ad1980.c
sound/soc/codecs/ad73311.c
sound/soc/codecs/ak4535.c
sound/soc/codecs/cs4270.c
sound/soc/codecs/l3.c [new file with mode: 0644]
sound/soc/codecs/pcm3008.c [new file with mode: 0644]
sound/soc/codecs/pcm3008.h [new file with mode: 0644]
sound/soc/codecs/ssm2602.c
sound/soc/codecs/tlv320aic23.c
sound/soc/codecs/tlv320aic26.c
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/tlv320aic3x.h
sound/soc/codecs/twl4030.c [new file with mode: 0644]
sound/soc/codecs/twl4030.h [new file with mode: 0644]
sound/soc/codecs/uda134x.c [new file with mode: 0644]
sound/soc/codecs/uda134x.h [new file with mode: 0644]
sound/soc/codecs/uda1380.c
sound/soc/codecs/wm8350.c [new file with mode: 0644]
sound/soc/codecs/wm8350.h [new file with mode: 0644]
sound/soc/codecs/wm8510.c
sound/soc/codecs/wm8580.c
sound/soc/codecs/wm8580.h
sound/soc/codecs/wm8728.c [new file with mode: 0644]
sound/soc/codecs/wm8728.h [new file with mode: 0644]
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8750.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8900.c
sound/soc/codecs/wm8900.h
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8903.h
sound/soc/codecs/wm8971.c
sound/soc/codecs/wm8990.c
sound/soc/codecs/wm8990.h
sound/soc/codecs/wm9712.c
sound/soc/codecs/wm9713.c
sound/soc/davinci/Kconfig
sound/soc/davinci/Makefile
sound/soc/davinci/davinci-evm.c
sound/soc/davinci/davinci-i2s.c
sound/soc/davinci/davinci-pcm.c
sound/soc/davinci/davinci-sffsdr.c [new file with mode: 0644]
sound/soc/fsl/Kconfig
sound/soc/fsl/fsl_dma.c
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/mpc5200_psc_i2s.c
sound/soc/fsl/mpc8610_hpcd.c
sound/soc/fsl/soc-of-simple.c
sound/soc/omap/Kconfig
sound/soc/omap/Makefile
sound/soc/omap/n810.c
sound/soc/omap/omap-mcbsp.c
sound/soc/omap/omap-pcm.c
sound/soc/omap/omap2evm.c [new file with mode: 0644]
sound/soc/omap/omap3beagle.c [new file with mode: 0644]
sound/soc/omap/omap3pandora.c [new file with mode: 0644]
sound/soc/omap/osk5912.c
sound/soc/omap/overo.c [new file with mode: 0644]
sound/soc/omap/sdp3430.c [new file with mode: 0644]
sound/soc/pxa/Kconfig
sound/soc/pxa/Makefile
sound/soc/pxa/corgi.c
sound/soc/pxa/e800_wm9712.c
sound/soc/pxa/em-x270.c
sound/soc/pxa/palm27x.c [new file with mode: 0644]
sound/soc/pxa/poodle.c
sound/soc/pxa/pxa-ssp.c [new file with mode: 0644]
sound/soc/pxa/pxa-ssp.h [new file with mode: 0644]
sound/soc/pxa/pxa2xx-ac97.c
sound/soc/pxa/pxa2xx-i2s.c
sound/soc/pxa/pxa2xx-pcm.c
sound/soc/pxa/spitz.c
sound/soc/pxa/tosa.c
sound/soc/pxa/zylonite.c [new file with mode: 0644]
sound/soc/s3c24xx/Kconfig
sound/soc/s3c24xx/Makefile
sound/soc/s3c24xx/ln2440sbc_alc650.c
sound/soc/s3c24xx/neo1973_wm8753.c
sound/soc/s3c24xx/s3c2412-i2s.c
sound/soc/s3c24xx/s3c2443-ac97.c
sound/soc/s3c24xx/s3c24xx-i2s.c
sound/soc/s3c24xx/s3c24xx-pcm.c
sound/soc/s3c24xx/s3c24xx_uda134x.c [new file with mode: 0644]
sound/soc/s3c24xx/smdk2443_wm9710.c
sound/soc/sh/dma-sh7760.c
sound/soc/sh/hac.c
sound/soc/sh/sh7760-ac97.c
sound/soc/sh/ssi.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/sound_core.c
sound/usb/caiaq/caiaq-control.c
sound/usb/caiaq/caiaq-device.c
sound/usb/usbmidi.c
sound/usb/usx2y/usb_stream.c

index 4f3f384..e3443dd 100644 (file)
@@ -93,10 +93,8 @@ Several "PowerBook" and "iBook2" notebooks are supported.
 1.5 SuperH
 ----------
 
-The following SuperH processors are supported by cpufreq:
-
-SH-3
-SH-4
+All SuperH processors supporting rate rounding through the clock
+framework are supported by cpufreq.
 
 1.6 Blackfin
 ------------
diff --git a/Documentation/credentials.txt b/Documentation/credentials.txt
new file mode 100644 (file)
index 0000000..df03169
--- /dev/null
@@ -0,0 +1,582 @@
+                            ====================
+                            CREDENTIALS IN LINUX
+                            ====================
+
+By: David Howells <dhowells@redhat.com>
+
+Contents:
+
+ (*) Overview.
+
+ (*) Types of credentials.
+
+ (*) File markings.
+
+ (*) Task credentials.
+
+     - Immutable credentials.
+     - Accessing task credentials.
+     - Accessing another task's credentials.
+     - Altering credentials.
+     - Managing credentials.
+
+ (*) Open file credentials.
+
+ (*) Overriding the VFS's use of credentials.
+
+
+========
+OVERVIEW
+========
+
+There are several parts to the security check performed by Linux when one
+object acts upon another:
+
+ (1) Objects.
+
+     Objects are things in the system that may be acted upon directly by
+     userspace programs.  Linux has a variety of actionable objects, including:
+
+       - Tasks
+       - Files/inodes
+       - Sockets
+       - Message queues
+       - Shared memory segments
+       - Semaphores
+       - Keys
+
+     As a part of the description of all these objects there is a set of
+     credentials.  What's in the set depends on the type of object.
+
+ (2) Object ownership.
+
+     Amongst the credentials of most objects, there will be a subset that
+     indicates the ownership of that object.  This is used for resource
+     accounting and limitation (disk quotas and task rlimits for example).
+
+     In a standard UNIX filesystem, for instance, this will be defined by the
+     UID marked on the inode.
+
+ (3) The objective context.
+
+     Also amongst the credentials of those objects, there will be a subset that
+     indicates the 'objective context' of that object.  This may or may not be
+     the same set as in (2) - in standard UNIX files, for instance, this is the
+     defined by the UID and the GID marked on the inode.
+
+     The objective context is used as part of the security calculation that is
+     carried out when an object is acted upon.
+
+ (4) Subjects.
+
+     A subject is an object that is acting upon another object.
+
+     Most of the objects in the system are inactive: they don't act on other
+     objects within the system.  Processes/tasks are the obvious exception:
+     they do stuff; they access and manipulate things.
+
+     Objects other than tasks may under some circumstances also be subjects.
+     For instance an open file may send SIGIO to a task using the UID and EUID
+     given to it by a task that called fcntl(F_SETOWN) upon it.  In this case,
+     the file struct will have a subjective context too.
+
+ (5) The subjective context.
+
+     A subject has an additional interpretation of its credentials.  A subset
+     of its credentials forms the 'subjective context'.  The subjective context
+     is used as part of the security calculation that is carried out when a
+     subject acts.
+
+     A Linux task, for example, has the FSUID, FSGID and the supplementary
+     group list for when it is acting upon a file - which are quite separate
+     from the real UID and GID that normally form the objective context of the
+     task.
+
+ (6) Actions.
+
+     Linux has a number of actions available that a subject may perform upon an
+     object.  The set of actions available depends on the nature of the subject
+     and the object.
+
+     Actions include reading, writing, creating and deleting files; forking or
+     signalling and tracing tasks.
+
+ (7) Rules, access control lists and security calculations.
+
+     When a subject acts upon an object, a security calculation is made.  This
+     involves taking the subjective context, the objective context and the
+     action, and searching one or more sets of rules to see whether the subject
+     is granted or denied permission to act in the desired manner on the
+     object, given those contexts.
+
+     There are two main sources of rules:
+
+     (a) Discretionary access control (DAC):
+
+        Sometimes the object will include sets of rules as part of its
+        description.  This is an 'Access Control List' or 'ACL'.  A Linux
+        file may supply more than one ACL.
+
+        A traditional UNIX file, for example, includes a permissions mask that
+        is an abbreviated ACL with three fixed classes of subject ('user',
+        'group' and 'other'), each of which may be granted certain privileges
+        ('read', 'write' and 'execute' - whatever those map to for the object
+        in question).  UNIX file permissions do not allow the arbitrary
+        specification of subjects, however, and so are of limited use.
+
+        A Linux file might also sport a POSIX ACL.  This is a list of rules
+        that grants various permissions to arbitrary subjects.
+
+     (b) Mandatory access control (MAC):
+
+        The system as a whole may have one or more sets of rules that get
+        applied to all subjects and objects, regardless of their source.
+        SELinux and Smack are examples of this.
+
+        In the case of SELinux and Smack, each object is given a label as part
+        of its credentials.  When an action is requested, they take the
+        subject label, the object label and the action and look for a rule
+        that says that this action is either granted or denied.
+
+
+====================
+TYPES OF CREDENTIALS
+====================
+
+The Linux kernel supports the following types of credentials:
+
+ (1) Traditional UNIX credentials.
+
+       Real User ID
+       Real Group ID
+
+     The UID and GID are carried by most, if not all, Linux objects, even if in
+     some cases it has to be invented (FAT or CIFS files for example, which are
+     derived from Windows).  These (mostly) define the objective context of
+     that object, with tasks being slightly different in some cases.
+
+       Effective, Saved and FS User ID
+       Effective, Saved and FS Group ID
+       Supplementary groups
+
+     These are additional credentials used by tasks only.  Usually, an
+     EUID/EGID/GROUPS will be used as the subjective context, and real UID/GID
+     will be used as the objective.  For tasks, it should be noted that this is
+     not always true.
+
+ (2) Capabilities.
+
+       Set of permitted capabilities
+       Set of inheritable capabilities
+       Set of effective capabilities
+       Capability bounding set
+
+     These are only carried by tasks.  They indicate superior capabilities
+     granted piecemeal to a task that an ordinary task wouldn't otherwise have.
+     These are manipulated implicitly by changes to the traditional UNIX
+     credentials, but can also be manipulated directly by the capset() system
+     call.
+
+     The permitted capabilities are those caps that the process might grant
+     itself to its effective or permitted sets through capset().  This
+     inheritable set might also be so constrained.
+
+     The effective capabilities are the ones that a task is actually allowed to
+     make use of itself.
+
+     The inheritable capabilities are the ones that may get passed across
+     execve().
+
+     The bounding set limits the capabilities that may be inherited across
+     execve(), especially when a binary is executed that will execute as UID 0.
+
+ (3) Secure management flags (securebits).
+
+     These are only carried by tasks.  These govern the way the above
+     credentials are manipulated and inherited over certain operations such as
+     execve().  They aren't used directly as objective or subjective
+     credentials.
+
+ (4) Keys and keyrings.
+
+     These are only carried by tasks.  They carry and cache security tokens
+     that don't fit into the other standard UNIX credentials.  They are for
+     making such things as network filesystem keys available to the file
+     accesses performed by processes, without the necessity of ordinary
+     programs having to know about security details involved.
+
+     Keyrings are a special type of key.  They carry sets of other keys and can
+     be searched for the desired key.  Each process may subscribe to a number
+     of keyrings:
+
+       Per-thread keying
+       Per-process keyring
+       Per-session keyring
+
+     When a process accesses a key, if not already present, it will normally be
+     cached on one of these keyrings for future accesses to find.
+
+     For more information on using keys, see Documentation/keys.txt.
+
+ (5) LSM
+
+     The Linux Security Module allows extra controls to be placed over the
+     operations that a task may do.  Currently Linux supports two main
+     alternate LSM options: SELinux and Smack.
+
+     Both work by labelling the objects in a system and then applying sets of
+     rules (policies) that say what operations a task with one label may do to
+     an object with another label.
+
+ (6) AF_KEY
+
+     This is a socket-based approach to credential management for networking
+     stacks [RFC 2367].  It isn't discussed by this document as it doesn't
+     interact directly with task and file credentials; rather it keeps system
+     level credentials.
+
+
+When a file is opened, part of the opening task's subjective context is
+recorded in the file struct created.  This allows operations using that file
+struct to use those credentials instead of the subjective context of the task
+that issued the operation.  An example of this would be a file opened on a
+network filesystem where the credentials of the opened file should be presented
+to the server, regardless of who is actually doing a read or a write upon it.
+
+
+=============
+FILE MARKINGS
+=============
+
+Files on disk or obtained over the network may have annotations that form the
+objective security context of that file.  Depending on the type of filesystem,
+this may include one or more of the following:
+
+ (*) UNIX UID, GID, mode;
+
+ (*) Windows user ID;
+
+ (*) Access control list;
+
+ (*) LSM security label;
+
+ (*) UNIX exec privilege escalation bits (SUID/SGID);
+
+ (*) File capabilities exec privilege escalation bits.
+
+These are compared to the task's subjective security context, and certain
+operations allowed or disallowed as a result.  In the case of execve(), the
+privilege escalation bits come into play, and may allow the resulting process
+extra privileges, based on the annotations on the executable file.
+
+
+================
+TASK CREDENTIALS
+================
+
+In Linux, all of a task's credentials are held in (uid, gid) or through
+(groups, keys, LSM security) a refcounted structure of type 'struct cred'.
+Each task points to its credentials by a pointer called 'cred' in its
+task_struct.
+
+Once a set of credentials has been prepared and committed, it may not be
+changed, barring the following exceptions:
+
+ (1) its reference count may be changed;
+
+ (2) the reference count on the group_info struct it points to may be changed;
+
+ (3) the reference count on the security data it points to may be changed;
+
+ (4) the reference count on any keyrings it points to may be changed;
+
+ (5) any keyrings it points to may be revoked, expired or have their security
+     attributes changed; and
+
+ (6) the contents of any keyrings to which it points may be changed (the whole
+     point of keyrings being a shared set of credentials, modifiable by anyone
+     with appropriate access).
+
+To alter anything in the cred struct, the copy-and-replace principle must be
+adhered to.  First take a copy, then alter the copy and then use RCU to change
+the task pointer to make it point to the new copy.  There are wrappers to aid
+with this (see below).
+
+A task may only alter its _own_ credentials; it is no longer permitted for a
+task to alter another's credentials.  This means the capset() system call is no
+longer permitted to take any PID other than the one of the current process.
+Also keyctl_instantiate() and keyctl_negate() functions no longer permit
+attachment to process-specific keyrings in the requesting process as the
+instantiating process may need to create them.
+
+
+IMMUTABLE CREDENTIALS
+---------------------
+
+Once a set of credentials has been made public (by calling commit_creds() for
+example), it must be considered immutable, barring two exceptions:
+
+ (1) The reference count may be altered.
+
+ (2) Whilst the keyring subscriptions of a set of credentials may not be
+     changed, the keyrings subscribed to may have their contents altered.
+
+To catch accidental credential alteration at compile time, struct task_struct
+has _const_ pointers to its credential sets, as does struct file.  Furthermore,
+certain functions such as get_cred() and put_cred() operate on const pointers,
+thus rendering casts unnecessary, but require to temporarily ditch the const
+qualification to be able to alter the reference count.
+
+
+ACCESSING TASK CREDENTIALS
+--------------------------
+
+A task being able to alter only its own credentials permits the current process
+to read or replace its own credentials without the need for any form of locking
+- which simplifies things greatly.  It can just call:
+
+       const struct cred *current_cred()
+
+to get a pointer to its credentials structure, and it doesn't have to release
+it afterwards.
+
+There are convenience wrappers for retrieving specific aspects of a task's
+credentials (the value is simply returned in each case):
+
+       uid_t current_uid(void)         Current's real UID
+       gid_t current_gid(void)         Current's real GID
+       uid_t current_euid(void)        Current's effective UID
+       gid_t current_egid(void)        Current's effective GID
+       uid_t current_fsuid(void)       Current's file access UID
+       gid_t current_fsgid(void)       Current's file access GID
+       kernel_cap_t current_cap(void)  Current's effective capabilities
+       void *current_security(void)    Current's LSM security pointer
+       struct user_struct *current_user(void)  Current's user account
+
+There are also convenience wrappers for retrieving specific associated pairs of
+a task's credentials:
+
+       void current_uid_gid(uid_t *, gid_t *);
+       void current_euid_egid(uid_t *, gid_t *);
+       void current_fsuid_fsgid(uid_t *, gid_t *);
+
+which return these pairs of values through their arguments after retrieving
+them from the current task's credentials.
+
+
+In addition, there is a function for obtaining a reference on the current
+process's current set of credentials:
+
+       const struct cred *get_current_cred(void);
+
+and functions for getting references to one of the credentials that don't
+actually live in struct cred:
+
+       struct user_struct *get_current_user(void);
+       struct group_info *get_current_groups(void);
+
+which get references to the current process's user accounting structure and
+supplementary groups list respectively.
+
+Once a reference has been obtained, it must be released with put_cred(),
+free_uid() or put_group_info() as appropriate.
+
+
+ACCESSING ANOTHER TASK'S CREDENTIALS
+------------------------------------
+
+Whilst a task may access its own credentials without the need for locking, the
+same is not true of a task wanting to access another task's credentials.  It
+must use the RCU read lock and rcu_dereference().
+
+The rcu_dereference() is wrapped by:
+
+       const struct cred *__task_cred(struct task_struct *task);
+
+This should be used inside the RCU read lock, as in the following example:
+
+       void foo(struct task_struct *t, struct foo_data *f)
+       {
+               const struct cred *tcred;
+               ...
+               rcu_read_lock();
+               tcred = __task_cred(t);
+               f->uid = tcred->uid;
+               f->gid = tcred->gid;
+               f->groups = get_group_info(tcred->groups);
+               rcu_read_unlock();
+               ...
+       }
+
+A function need not get RCU read lock to use __task_cred() if it is holding a
+spinlock at the time as this implicitly holds the RCU read lock.
+
+Should it be necessary to hold another task's credentials for a long period of
+time, and possibly to sleep whilst doing so, then the caller should get a
+reference on them using:
+
+       const struct cred *get_task_cred(struct task_struct *task);
+
+This does all the RCU magic inside of it.  The caller must call put_cred() on
+the credentials so obtained when they're finished with.
+
+There are a couple of convenience functions to access bits of another task's
+credentials, hiding the RCU magic from the caller:
+
+       uid_t task_uid(task)            Task's real UID
+       uid_t task_euid(task)           Task's effective UID
+
+If the caller is holding a spinlock or the RCU read lock at the time anyway,
+then:
+
+       __task_cred(task)->uid
+       __task_cred(task)->euid
+
+should be used instead.  Similarly, if multiple aspects of a task's credentials
+need to be accessed, RCU read lock or a spinlock should be used, __task_cred()
+called, the result stored in a temporary pointer and then the credential
+aspects called from that before dropping the lock.  This prevents the
+potentially expensive RCU magic from being invoked multiple times.
+
+Should some other single aspect of another task's credentials need to be
+accessed, then this can be used:
+
+       task_cred_xxx(task, member)
+
+where 'member' is a non-pointer member of the cred struct.  For instance:
+
+       uid_t task_cred_xxx(task, suid);
+
+will retrieve 'struct cred::suid' from the task, doing the appropriate RCU
+magic.  This may not be used for pointer members as what they point to may
+disappear the moment the RCU read lock is dropped.
+
+
+ALTERING CREDENTIALS
+--------------------
+
+As previously mentioned, a task may only alter its own credentials, and may not
+alter those of another task.  This means that it doesn't need to use any
+locking to alter its own credentials.
+
+To alter the current process's credentials, a function should first prepare a
+new set of credentials by calling:
+
+       struct cred *prepare_creds(void);
+
+this locks current->cred_replace_mutex and then allocates and constructs a
+duplicate of the current process's credentials, returning with the mutex still
+held if successful.  It returns NULL if not successful (out of memory).
+
+The mutex prevents ptrace() from altering the ptrace state of a process whilst
+security checks on credentials construction and changing is taking place as
+the ptrace state may alter the outcome, particularly in the case of execve().
+
+The new credentials set should be altered appropriately, and any security
+checks and hooks done.  Both the current and the proposed sets of credentials
+are available for this purpose as current_cred() will return the current set
+still at this point.
+
+
+When the credential set is ready, it should be committed to the current process
+by calling:
+
+       int commit_creds(struct cred *new);
+
+This will alter various aspects of the credentials and the process, giving the
+LSM a chance to do likewise, then it will use rcu_assign_pointer() to actually
+commit the new credentials to current->cred, it will release
+current->cred_replace_mutex to allow ptrace() to take place, and it will notify
+the scheduler and others of the changes.
+
+This function is guaranteed to return 0, so that it can be tail-called at the
+end of such functions as sys_setresuid().
+
+Note that this function consumes the caller's reference to the new credentials.
+The caller should _not_ call put_cred() on the new credentials afterwards.
+
+Furthermore, once this function has been called on a new set of credentials,
+those credentials may _not_ be changed further.
+
+
+Should the security checks fail or some other error occur after prepare_creds()
+has been called, then the following function should be invoked:
+
+       void abort_creds(struct cred *new);
+
+This releases the lock on current->cred_replace_mutex that prepare_creds() got
+and then releases the new credentials.
+
+
+A typical credentials alteration function would look something like this:
+
+       int alter_suid(uid_t suid)
+       {
+               struct cred *new;
+               int ret;
+
+               new = prepare_creds();
+               if (!new)
+                       return -ENOMEM;
+
+               new->suid = suid;
+               ret = security_alter_suid(new);
+               if (ret < 0) {
+                       abort_creds(new);
+                       return ret;
+               }
+
+               return commit_creds(new);
+       }
+
+
+MANAGING CREDENTIALS
+--------------------
+
+There are some functions to help manage credentials:
+
+ (*) void put_cred(const struct cred *cred);
+
+     This releases a reference to the given set of credentials.  If the
+     reference count reaches zero, the credentials will be scheduled for
+     destruction by the RCU system.
+
+ (*) const struct cred *get_cred(const struct cred *cred);
+
+     This gets a reference on a live set of credentials, returning a pointer to
+     that set of credentials.
+
+ (*) struct cred *get_new_cred(struct cred *cred);
+
+     This gets a reference on a set of credentials that is under construction
+     and is thus still mutable, returning a pointer to that set of credentials.
+
+
+=====================
+OPEN FILE CREDENTIALS
+=====================
+
+When a new file is opened, a reference is obtained on the opening task's
+credentials and this is attached to the file struct as 'f_cred' in place of
+'f_uid' and 'f_gid'.  Code that used to access file->f_uid and file->f_gid
+should now access file->f_cred->fsuid and file->f_cred->fsgid.
+
+It is safe to access f_cred without the use of RCU or locking because the
+pointer will not change over the lifetime of the file struct, and nor will the
+contents of the cred struct pointed to, barring the exceptions listed above
+(see the Task Credentials section).
+
+
+=======================================
+OVERRIDING THE VFS'S USE OF CREDENTIALS
+=======================================
+
+Under some circumstances it is desirable to override the credentials used by
+the VFS, and that can be done by calling into such as vfs_mkdir() with a
+different set of credentials.  This is done in the following places:
+
+ (*) sys_faccessat().
+
+ (*) do_coredump().
+
+ (*) nfs4recover.c.
index ee5a5f9..2c95cae 100644 (file)
@@ -1465,6 +1465,10 @@ and is between 256 and 4096 characters. It is defined in the file
                        instruction doesn't work correctly and not to
                        use it.
 
+       no_file_caps    Tells the kernel not to honor file capabilities.  The
+                       only way then for a file to be executed with privilege
+                       is to be setuid root or executed by root.
+
        nohalt          [IA-64] Tells the kernel not to use the power saving
                        function PAL_HALT_LIGHT when idle. This increases
                        power-consumption. On the positive side, it reduces
index eb471c7..8398ca4 100644 (file)
@@ -273,3 +273,24 @@ task groups and modify their CPU share using the "cgroups" pseudo filesystem.
 
        # #Launch gmplayer (or your favourite movie player)
        # echo <movie_player_pid> > multimedia/tasks
+
+8. Implementation note: user namespaces
+
+User namespaces are intended to be hierarchical.  But they are currently
+only partially implemented.  Each of those has ramifications for CFS.
+
+First, since user namespaces are hierarchical, the /sys/kernel/uids
+presentation is inadequate.  Eventually we will likely want to use sysfs
+tagging to provide private views of /sys/kernel/uids within each user
+namespace.
+
+Second, the hierarchical nature is intended to support completely
+unprivileged use of user namespaces.  So if using user groups, then
+we want the users in a user namespace to be children of the user
+who created it.
+
+That is currently unimplemented.  So instead, every user in a new
+user namespace will receive 1024 shares just like any user in the
+initial user namespace.  Note that at the moment creation of a new
+user namespace requires each of CAP_SYS_ADMIN, CAP_SETUID, and
+CAP_SETGID.
diff --git a/Documentation/sh/kgdb.txt b/Documentation/sh/kgdb.txt
deleted file mode 100644 (file)
index 05b4ba8..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-
-This file describes the configuration and behavior of KGDB for the SH
-kernel. Based on a description from Henry Bell <henry.bell@st.com>, it
-has been modified to account for quirks in the current implementation.
-
-Version
-=======
-
-This version of KGDB was written for 2.4.xx kernels for the SH architecture.
-Further documentation is available from the linux-sh project website.
-
-
-Debugging Setup: Host
-======================
-
-The two machines will be connected together via a serial line - this
-should be a null modem cable i.e. with a twist.
-
-On your DEVELOPMENT machine, go to your kernel source directory and
-build the kernel, enabling KGDB support in the "kernel hacking" section.
-This includes the KGDB code, and also makes the kernel be compiled with
-the "-g" option set -- necessary for debugging.
-
-To install this new kernel, use the following installation procedure.
-
-Decide on which tty port you want the machines to communicate, then
-cable them up back-to-back using the null modem.  On the DEVELOPMENT
-machine, you may wish to create an initialization file called .gdbinit
-(in the kernel source directory or in your home directory) to execute 
-commonly-used commands at startup.
-
-A minimal .gdbinit might look like this:
-
-  file vmlinux
-  set remotebaud 115200
-  target remote /dev/ttyS0
-
-Change the "target" definition so that it specifies the tty port that
-you intend to use.  Change the "remotebaud" definition to match the
-data rate that you are going to use for the com line (115200 is the
-default). 
-
-Debugging Setup: Target
-========================
-
-By default, the KGDB stub will communicate with the host GDB using
-ttySC1 at 115200 baud, 8 databits, no parity; these defaults can be
-changed in the kernel configuration. As the kernel starts up, KGDB will
-initialize so that breakpoints, kernel segfaults, and so forth will
-generally enter the debugger.
-
-This behavior can be modified by including the "kgdb" option in the
-kernel command line; this option has the general form:
-
-  kgdb=<ttyspec>,<action>
-
-The <ttyspec> indicates the port to use, and can optionally specify
-baud, parity and databits -- e.g. "ttySC0,9600N8" or "ttySC1,19200".
-
-The <action> can be "halt" or "disabled".  The "halt" action enters the
-debugger via a breakpoint as soon as kgdb is initialized; the "disabled"
-action causes kgdb to ignore kernel segfaults and such until explicitly
-entered by a breakpoint in the code or by external action (sysrq or NMI). 
-
-(Both <ttyspec> and <action> can appear alone, w/o the separating comma.)
-
-For example, if you wish to debug early in kernel startup code, you
-might specify the halt option:
-
-  kgdb=halt
-
-Boot the TARGET machine, which will appear to hang. 
-
-On your DEVELOPMENT machine, cd to the source directory and run the gdb
-program.  (This is likely to be a cross GDB which runs on your host but
-is built for an SH target.) If everything is working correctly you
-should see gdb print out a few lines indicating that a breakpoint has
-been taken.  It will actually show a line of code in the target kernel
-inside the gdbstub activation code.
-
-NOTE: BE SURE TO TERMINATE OR SUSPEND any other host application which
-may be using the same serial port (for example, a terminal emulator you
-have been using to connect to the target boot code.)  Otherwise, data
-from the target may not all get to GDB!
-
-You can now use whatever gdb commands you like to set breakpoints.
-Enter "continue" to start your target machine executing again.  At this
-point the target system will run at full speed until it encounters
-your breakpoint or gets a segment violation in the kernel, or whatever.
-
-Serial Ports: KGDB, Console
-============================
-
-This version of KGDB may not gracefully handle conflict with other
-drivers in the kernel using the same port. If KGDB is configured on the
-same port (and with the same parameters) as the kernel console, or if
-CONFIG_SH_KGDB_CONSOLE is configured, things should be fine (though in
-some cases console messages may appear twice through GDB).  But if the
-KGDB port is not the kernel console and used by another serial driver
-which assumes different serial parameters (e.g. baud rate) KGDB may not
-recover.
-
-Also, when KGDB is entered via sysrq-g (requires CONFIG_KGDB_SYSRQ) and
-the kgdb port uses the same port as the console, detaching GDB will not
-restore the console to working order without the port being re-opened.
-
-Another serious consequence of this is that GDB currently CANNOT break
-into KGDB externally (e.g. via ^C or <BREAK>); unless a breakpoint or
-error is encountered, the only way to enter KGDB after the initial halt
-(see above) is via NMI (CONFIG_KGDB_NMI) or sysrq-g (CONFIG_KGDB_SYSRQ).
-
-Code is included for the basic Hitachi Solution Engine boards to allow
-the use of ttyS0 for KGDB if desired; this is less robust, but may be
-useful in some cases.  (This cannot be selected using the config file, 
-but only through the kernel command line, e.g. "kgdb=ttyS0", though the
-configured defaults for baud rate etc. still apply if not overridden.)
-
-If gdbstub Does Not Work
-========================
-
-If it doesn't work, you will have to troubleshoot it.  Do the easy
-things first like double checking your cabling and data rates.  You
-might try some non-kernel based programs to see if the back-to-back
-connection works properly.  Just something simple like cat /etc/hosts
-/dev/ttyS0 on one machine and cat /dev/ttyS0 on the other will tell you
-if you can send data from one machine to the other.  There is no point
-in tearing out your hair in the kernel if the line doesn't work.
-
-If you need to debug the GDB/KGDB communication itself, the gdb commands
-"set debug remote 1" and "set debug serial 1" may be useful, but be
-warned: they produce a lot of output.
-
-Threads
-=======
-
-Each process in a target machine is seen as a gdb thread. gdb thread related
-commands (info threads, thread n) can be used. CONFIG_KGDB_THREAD must
-be defined for this to work.
-
-In this version, kgdb reports PID_MAX (32768) as the process ID for the
-idle process (pid 0), since GDB does not accept 0 as an ID.
-
-Detaching (exiting KGDB)
-=========================
-
-There are two ways to resume full-speed target execution: "continue" and
-"detach". With "continue", GDB inserts any specified breakpoints in the
-target code and resumes execution; the target is still in "gdb mode".
-If a breakpoint or other debug event (e.g. NMI) happens, the target
-halts and communicates with GDB again, which is waiting for it.
-
-With "detach", GDB does *not* insert any breakpoints; target execution
-is resumed and GDB stops communicating (does not wait for the target).
-In this case, the target is no longer in "gdb mode" -- for example,
-console messages no longer get sent separately to the KGDB port, or
-encapsulated for GDB.  If a debug event (e.g. NMI) occurs, the target
-will re-enter "gdb mode" and will display this fact on the console; you
-must give a new "target remote" command to gdb.
-
-NOTE: TO AVOID LOSSING CONSOLE MESSAGES IN CASE THE KERNEL CONSOLE AND
-KGDB USING THE SAME PORT, THE TARGET WAITS FOR ANY INPUT CHARACTER ON
-THE KGDB PORT AFTER A DETACH COMMAND.  For example, after the detach you
-could start a terminal emulator on the same host port and enter a <cr>;
-however, this program must then be terminated or suspended in order to
-use GBD again if KGDB is re-entered.
-
-
-Acknowledgements
-================
-
-This code was mostly generated by Henry Bell <henry.bell@st.com>;
-largely from KGDB by Amit S. Kale <akale@veritas.com> - extracts from
-code by Glenn Engel, Jim Kingdon, David Grothe <dave@gcom.com>, Tigran
-Aivazian <tigran@sco.com>, William Gatliff <bgat@open-widgets.com>, Ben
-Lee, Steve Chamberlain and Benoit Miller <fulg@iname.com> are also
-included. 
-
-Jeremy Siegel
-<jsiegel@mvista.com>
index 394d7d3..841a936 100644 (file)
@@ -757,6 +757,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     model      - force the model name
     position_fix - Fix DMA pointer (0 = auto, 1 = use LPIB, 2 = POSBUF)
     probe_mask  - Bitmask to probe codecs (default = -1, meaning all slots)
+    probe_only - Only probing and no codec initialization (default=off);
+                 Useful to check the initial codec status for debugging
     bdl_pos_adj        - Specifies the DMA IRQ timing delay in samples.
                Passing -1 will make the driver to choose the appropriate
                value based on the controller chip.
@@ -772,327 +774,23 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     This module supports multiple cards and autoprobe.
     
+    See Documentation/sound/alsa/HD-Audio.txt for more details about
+    HD-audio driver.
+
     Each codec may have a model table for different configurations.
     If your machine isn't listed there, the default (usually minimal)
     configuration is set up.  You can pass "model=<name>" option to
     specify a certain model in such a case.  There are different
-    models depending on the codec chip.
-
-         Model name    Description
-         ----------    -----------
-       ALC880
-         3stack        3-jack in back and a headphone out
-         3stack-digout 3-jack in back, a HP out and a SPDIF out
-         5stack        5-jack in back, 2-jack in front
-         5stack-digout 5-jack in back, 2-jack in front, a SPDIF out
-         6stack        6-jack in back, 2-jack in front
-         6stack-digout 6-jack with a SPDIF out
-         w810          3-jack
-         z71v          3-jack (HP shared SPDIF)
-         asus          3-jack (ASUS Mobo)
-         asus-w1v      ASUS W1V
-         asus-dig      ASUS with SPDIF out
-         asus-dig2     ASUS with SPDIF out (using GPIO2)
-         uniwill       3-jack
-         fujitsu       Fujitsu Laptops (Pi1536)
-         F1734         2-jack
-         lg            LG laptop (m1 express dual)
-         lg-lw         LG LW20/LW25 laptop
-         tcl           TCL S700
-         clevo         Clevo laptops (m520G, m665n)
-         medion        Medion Rim 2150
-         test          for testing/debugging purpose, almost all controls can be
-                       adjusted.  Appearing only when compiled with
-                       $CONFIG_SND_DEBUG=y
-         auto          auto-config reading BIOS (default)
-
-       ALC260
-         hp            HP machines
-         hp-3013       HP machines (3013-variant)
-         hp-dc7600     HP DC7600
-         fujitsu       Fujitsu S7020
-         acer          Acer TravelMate
-         will          Will laptops (PB V7900)
-         replacer      Replacer 672V
-         basic         fixed pin assignment (old default model)
-         test          for testing/debugging purpose, almost all controls can
-                       adjusted.  Appearing only when compiled with
-                       $CONFIG_SND_DEBUG=y
-         auto          auto-config reading BIOS (default)
-
-       ALC262
-         fujitsu       Fujitsu Laptop
-         hp-bpc        HP xw4400/6400/8400/9400 laptops
-         hp-bpc-d7000  HP BPC D7000
-         hp-tc-t5735   HP Thin Client T5735
-         hp-rp5700     HP RP5700
-         benq          Benq ED8
-         benq-t31      Benq T31
-         hippo         Hippo (ATI) with jack detection, Sony UX-90s
-         hippo_1       Hippo (Benq) with jack detection
-         sony-assamd   Sony ASSAMD
-         toshiba-s06   Toshiba S06
-         toshiba-rx1   Toshiba RX1
-         ultra         Samsung Q1 Ultra Vista model
-         lenovo-3000   Lenovo 3000 y410
-         nec           NEC Versa S9100
-         basic         fixed pin assignment w/o SPDIF
-         auto          auto-config reading BIOS (default)
-
-       ALC267/268
-         quanta-il1    Quanta IL1 mini-notebook
-         3stack        3-stack model
-         toshiba       Toshiba A205
-         acer          Acer laptops
-         acer-aspire   Acer Aspire One
-         dell          Dell OEM laptops (Vostro 1200)
-         zepto         Zepto laptops
-         test          for testing/debugging purpose, almost all controls can
-                       adjusted.  Appearing only when compiled with
-                       $CONFIG_SND_DEBUG=y
-         auto          auto-config reading BIOS (default)
-
-       ALC269
-         basic         Basic preset
-         quanta        Quanta FL1
-         eeepc-p703    ASUS Eeepc P703 P900A
-         eeepc-p901    ASUS Eeepc P901 S101
-
-       ALC662/663
-         3stack-dig    3-stack (2-channel) with SPDIF
-         3stack-6ch     3-stack (6-channel)
-         3stack-6ch-dig 3-stack (6-channel) with SPDIF
-         6stack-dig     6-stack with SPDIF
-         lenovo-101e    Lenovo laptop
-         eeepc-p701    ASUS Eeepc P701
-         eeepc-ep20    ASUS Eeepc EP20
-         ecs           ECS/Foxconn mobo
-         m51va         ASUS M51VA
-         g71v          ASUS G71V
-         h13           ASUS H13
-         g50v          ASUS G50V
-         asus-mode1    ASUS
-         asus-mode2    ASUS
-         asus-mode3    ASUS
-         asus-mode4    ASUS
-         asus-mode5    ASUS
-         asus-mode6    ASUS
-         auto          auto-config reading BIOS (default)
-
-       ALC882/885
-         3stack-dig    3-jack with SPDIF I/O
-         6stack-dig    6-jack digital with SPDIF I/O
-         arima         Arima W820Di1
-         targa         Targa T8, MSI-1049 T8
-         asus-a7j      ASUS A7J
-         asus-a7m      ASUS A7M
-         macpro        MacPro support
-         mbp3          Macbook Pro rev3
-         imac24        iMac 24'' with jack detection
-         w2jc          ASUS W2JC
-         auto          auto-config reading BIOS (default)
-
-       ALC883/888
-         3stack-dig    3-jack with SPDIF I/O
-         6stack-dig    6-jack digital with SPDIF I/O
-         3stack-6ch    3-jack 6-channel
-         3stack-6ch-dig 3-jack 6-channel with SPDIF I/O
-         6stack-dig-demo  6-jack digital for Intel demo board
-         acer          Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
-         acer-aspire   Acer Aspire 9810
-         medion        Medion Laptops
-         medion-md2    Medion MD2
-         targa-dig     Targa/MSI
-         targa-2ch-dig Targs/MSI with 2-channel
-         laptop-eapd   3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE)
-         lenovo-101e   Lenovo 101E
-         lenovo-nb0763 Lenovo NB0763
-         lenovo-ms7195-dig Lenovo MS7195
-         lenovo-sky    Lenovo Sky
-         haier-w66     Haier W66
-         3stack-hp     HP machines with 3stack (Lucknow, Samba boards)
-         6stack-dell   Dell machines with 6stack (Inspiron 530)
-         mitac         Mitac 8252D
-         clevo-m720    Clevo M720 laptop series
-         fujitsu-pi2515 Fujitsu AMILO Pi2515
-         3stack-6ch-intel Intel DG33* boards
-         auto          auto-config reading BIOS (default)
-
-       ALC861/660
-         3stack        3-jack
-         3stack-dig    3-jack with SPDIF I/O
-         6stack-dig    6-jack with SPDIF I/O
-         3stack-660    3-jack (for ALC660)
-         uniwill-m31   Uniwill M31 laptop
-         toshiba       Toshiba laptop support
-         asus          Asus laptop support
-         asus-laptop   ASUS F2/F3 laptops
-         auto          auto-config reading BIOS (default)
-
-       ALC861VD/660VD
-         3stack        3-jack
-         3stack-dig    3-jack with SPDIF OUT
-         6stack-dig    6-jack with SPDIF OUT
-         3stack-660    3-jack (for ALC660VD)
-         3stack-660-digout 3-jack with SPDIF OUT (for ALC660VD)
-         lenovo        Lenovo 3000 C200
-         dallas        Dallas laptops
-         hp            HP TX1000
-         auto          auto-config reading BIOS (default)
-
-       CMI9880
-         minimal       3-jack in back
-         min_fp        3-jack in back, 2-jack in front
-         full          6-jack in back, 2-jack in front
-         full_dig      6-jack in back, 2-jack in front, SPDIF I/O
-         allout        5-jack in back, 2-jack in front, SPDIF out
-         auto          auto-config reading BIOS (default)
-
-       AD1882 / AD1882A
-         3stack        3-stack mode (default)
-         6stack        6-stack mode
-
-       AD1884A / AD1883 / AD1984A / AD1984B
-         desktop       3-stack desktop (default)
-         laptop        laptop with HP jack sensing
-         mobile        mobile devices with HP jack sensing
-         thinkpad      Lenovo Thinkpad X300
-
-       AD1884
-         N/A
-
-       AD1981
-         basic         3-jack (default)
-         hp            HP nx6320
-         thinkpad      Lenovo Thinkpad T60/X60/Z60
-         toshiba       Toshiba U205
-
-       AD1983
-         N/A
-
-       AD1984
-         basic         default configuration
-         thinkpad      Lenovo Thinkpad T61/X61
-         dell          Dell T3400
-
-       AD1986A
-         6stack        6-jack, separate surrounds (default)
-         3stack        3-stack, shared surrounds
-         laptop        2-channel only (FSC V2060, Samsung M50)
-         laptop-eapd   2-channel with EAPD (Samsung R65, ASUS A6J)
-         laptop-automute 2-channel with EAPD and HP-automute (Lenovo N100)
-         ultra         2-channel with EAPD (Samsung Ultra tablet PC)
-
-       AD1988/AD1988B/AD1989A/AD1989B
-         6stack        6-jack
-         6stack-dig    ditto with SPDIF
-         3stack        3-jack
-         3stack-dig    ditto with SPDIF
-         laptop        3-jack with hp-jack automute
-         laptop-dig    ditto with SPDIF
-         auto          auto-config reading BIOS (default)
-       
-       Conexant 5045
-         laptop-hpsense    Laptop with HP sense (old model laptop)
-         laptop-micsense   Laptop with Mic sense (old model fujitsu)
-         laptop-hpmicsense Laptop with HP and Mic senses
-         benq          Benq R55E
-         test          for testing/debugging purpose, almost all controls
-                       can be adjusted.  Appearing only when compiled with
-                       $CONFIG_SND_DEBUG=y
-
-       Conexant 5047
-         laptop        Basic Laptop config 
-         laptop-hp     Laptop config for some HP models (subdevice 30A5)
-         laptop-eapd   Laptop config with EAPD support
-         test          for testing/debugging purpose, almost all controls
-                       can be adjusted.  Appearing only when compiled with
-                       $CONFIG_SND_DEBUG=y
-
-       Conexant 5051
-         laptop        Basic Laptop config (default)
-         hp            HP Spartan laptop
-
-       STAC9200
-         ref           Reference board
-         dell-d21      Dell (unknown)
-         dell-d22      Dell (unknown)
-         dell-d23      Dell (unknown)
-         dell-m21      Dell Inspiron 630m, Dell Inspiron 640m
-         dell-m22      Dell Latitude D620, Dell Latitude D820
-         dell-m23      Dell XPS M1710, Dell Precision M90
-         dell-m24      Dell Latitude 120L
-         dell-m25      Dell Inspiron E1505n
-         dell-m26      Dell Inspiron 1501
-         dell-m27      Dell Inspiron E1705/9400
-         gateway       Gateway laptops with EAPD control
-         panasonic     Panasonic CF-74
-
-       STAC9205/9254
-         ref           Reference board
-         dell-m42      Dell (unknown)
-         dell-m43      Dell Precision
-         dell-m44      Dell Inspiron
-
-       STAC9220/9221
-         ref           Reference board
-         3stack        D945 3stack
-         5stack        D945 5stack + SPDIF
-         intel-mac-v1  Intel Mac Type 1
-         intel-mac-v2  Intel Mac Type 2
-         intel-mac-v3  Intel Mac Type 3
-         intel-mac-v4  Intel Mac Type 4
-         intel-mac-v5  Intel Mac Type 5
-         intel-mac-auto Intel Mac (detect type according to subsystem id)
-         macmini       Intel Mac Mini (equivalent with type 3)
-         macbook       Intel Mac Book (eq. type 5)
-         macbook-pro-v1 Intel Mac Book Pro 1st generation (eq. type 3)
-         macbook-pro   Intel Mac Book Pro 2nd generation (eq. type 3)
-         imac-intel    Intel iMac (eq. type 2)
-         imac-intel-20 Intel iMac (newer version) (eq. type 3)
-         dell-d81      Dell (unknown)
-         dell-d82      Dell (unknown)
-         dell-m81      Dell (unknown)
-         dell-m82      Dell XPS M1210
-
-       STAC9202/9250/9251
-         ref           Reference board, base config
-         m2-2          Some Gateway MX series laptops
-         m6            Some Gateway NX series laptops
-         pa6           Gateway NX860 series
-
-       STAC9227/9228/9229/927x
-         ref           Reference board
-         ref-no-jd     Reference board without HP/Mic jack detection
-         3stack        D965 3stack
-         5stack        D965 5stack + SPDIF
-         dell-3stack   Dell Dimension E520
-         dell-bios     Fixes with Dell BIOS setup
-
-       STAC92HD71B*
-         ref           Reference board
-         dell-m4-1     Dell desktops
-         dell-m4-2     Dell desktops
-         dell-m4-3     Dell desktops
-
-       STAC92HD73*
-         ref           Reference board
-         no-jd         BIOS setup but without jack-detection
-         dell-m6-amic  Dell desktops/laptops with analog mics
-         dell-m6-dmic  Dell desktops/laptops with digital mics
-         dell-m6       Dell desktops/laptops with both type of mics
-
-       STAC9872
-         vaio          Setup for VAIO FE550G/SZ110
-         vaio-ar Setup for VAIO AR
+    models depending on the codec chip.  The list of available models
+    is found in HD-Audio-Models.txt
 
     The model name "genric" is treated as a special case.  When this
     model is given, the driver uses the generic codec parser without
     "codec-patch".  It's sometimes good for testing and debugging.
 
     If the default configuration doesn't work and one of the above
-    matches with your device, report it together with the PCI
-    subsystem ID (output of "lspci -nv") to ALSA BTS or alsa-devel
+    matches with your device, report it together with alsa-info.sh
+    output (with --no-upload option) to kernel bugzilla or alsa-devel
     ML (see the section "Links and Addresses").
 
     power_save and power_save_controller options are for power-saving
@@ -1652,7 +1350,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     * AuzenTech X-Meridian
     * Bgears b-Enspirer
     * Club3D Theatron DTS
-    * HT-Omega Claro
+    * HT-Omega Claro (plus)
+    * HT-Omega Claro halo (XT)
     * Razer Barracuda AC-1
     * Sondigo Inferno
 
@@ -2409,8 +2108,11 @@ Links and Addresses
   ALSA project homepage
        http://www.alsa-project.org
 
-  ALSA Bug Tracking System
-       https://bugtrack.alsa-project.org/bugs/
+  Kernel Bugzilla
+       http://bugzilla.kernel.org/
 
   ALSA Developers ML
        mailto:alsa-devel@alsa-project.org
+
+  alsa-info.sh script
+       http://www.alsa-project.org/alsa-info.sh
diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt
new file mode 100644 (file)
index 0000000..4b7ac21
--- /dev/null
@@ -0,0 +1,348 @@
+  Model name   Description
+  ----------    -----------
+ALC880
+======
+  3stack       3-jack in back and a headphone out
+  3stack-digout        3-jack in back, a HP out and a SPDIF out
+  5stack       5-jack in back, 2-jack in front
+  5stack-digout        5-jack in back, 2-jack in front, a SPDIF out
+  6stack       6-jack in back, 2-jack in front
+  6stack-digout        6-jack with a SPDIF out
+  w810         3-jack
+  z71v         3-jack (HP shared SPDIF)
+  asus         3-jack (ASUS Mobo)
+  asus-w1v     ASUS W1V
+  asus-dig     ASUS with SPDIF out
+  asus-dig2    ASUS with SPDIF out (using GPIO2)
+  uniwill      3-jack
+  fujitsu      Fujitsu Laptops (Pi1536)
+  F1734                2-jack
+  lg           LG laptop (m1 express dual)
+  lg-lw                LG LW20/LW25 laptop
+  tcl          TCL S700
+  clevo                Clevo laptops (m520G, m665n)
+  medion       Medion Rim 2150
+  test         for testing/debugging purpose, almost all controls can be
+               adjusted.  Appearing only when compiled with
+               $CONFIG_SND_DEBUG=y
+  auto         auto-config reading BIOS (default)
+
+ALC260
+======
+  hp           HP machines
+  hp-3013      HP machines (3013-variant)
+  hp-dc7600    HP DC7600
+  fujitsu      Fujitsu S7020
+  acer         Acer TravelMate
+  will         Will laptops (PB V7900)
+  replacer     Replacer 672V
+  basic                fixed pin assignment (old default model)
+  test         for testing/debugging purpose, almost all controls can
+               adjusted.  Appearing only when compiled with
+               $CONFIG_SND_DEBUG=y
+  auto         auto-config reading BIOS (default)
+
+ALC262
+======
+  fujitsu      Fujitsu Laptop
+  hp-bpc       HP xw4400/6400/8400/9400 laptops
+  hp-bpc-d7000 HP BPC D7000
+  hp-tc-t5735  HP Thin Client T5735
+  hp-rp5700    HP RP5700
+  benq         Benq ED8
+  benq-t31     Benq T31
+  hippo                Hippo (ATI) with jack detection, Sony UX-90s
+  hippo_1      Hippo (Benq) with jack detection
+  sony-assamd  Sony ASSAMD
+  toshiba-s06  Toshiba S06
+  toshiba-rx1  Toshiba RX1
+  ultra                Samsung Q1 Ultra Vista model
+  lenovo-3000  Lenovo 3000 y410
+  nec          NEC Versa S9100
+  basic                fixed pin assignment w/o SPDIF
+  auto         auto-config reading BIOS (default)
+
+ALC267/268
+==========
+  quanta-il1   Quanta IL1 mini-notebook
+  3stack       3-stack model
+  toshiba      Toshiba A205
+  acer         Acer laptops
+  acer-dmic    Acer laptops with digital-mic
+  acer-aspire  Acer Aspire One
+  dell         Dell OEM laptops (Vostro 1200)
+  zepto                Zepto laptops
+  test         for testing/debugging purpose, almost all controls can
+               adjusted.  Appearing only when compiled with
+               $CONFIG_SND_DEBUG=y
+  auto         auto-config reading BIOS (default)
+
+ALC269
+======
+  basic                Basic preset
+  quanta       Quanta FL1
+  eeepc-p703   ASUS Eeepc P703 P900A
+  eeepc-p901   ASUS Eeepc P901 S101
+  fujitsu      FSC Amilo
+  auto         auto-config reading BIOS (default)
+
+ALC662/663
+==========
+  3stack-dig   3-stack (2-channel) with SPDIF
+  3stack-6ch    3-stack (6-channel)
+  3stack-6ch-dig 3-stack (6-channel) with SPDIF
+  6stack-dig    6-stack with SPDIF
+  lenovo-101e   Lenovo laptop
+  eeepc-p701   ASUS Eeepc P701
+  eeepc-ep20   ASUS Eeepc EP20
+  ecs          ECS/Foxconn mobo
+  m51va                ASUS M51VA
+  g71v         ASUS G71V
+  h13          ASUS H13
+  g50v         ASUS G50V
+  asus-mode1   ASUS
+  asus-mode2   ASUS
+  asus-mode3   ASUS
+  asus-mode4   ASUS
+  asus-mode5   ASUS
+  asus-mode6   ASUS
+  auto         auto-config reading BIOS (default)
+
+ALC882/885
+==========
+  3stack-dig   3-jack with SPDIF I/O
+  6stack-dig   6-jack digital with SPDIF I/O
+  arima                Arima W820Di1
+  targa                Targa T8, MSI-1049 T8
+  asus-a7j     ASUS A7J
+  asus-a7m     ASUS A7M
+  macpro       MacPro support
+  mbp3         Macbook Pro rev3
+  imac24       iMac 24'' with jack detection
+  w2jc         ASUS W2JC
+  auto         auto-config reading BIOS (default)
+
+ALC883/888
+==========
+  3stack-dig   3-jack with SPDIF I/O
+  6stack-dig   6-jack digital with SPDIF I/O
+  3stack-6ch    3-jack 6-channel
+  3stack-6ch-dig 3-jack 6-channel with SPDIF I/O
+  6stack-dig-demo  6-jack digital for Intel demo board
+  acer         Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
+  acer-aspire  Acer Aspire 9810
+  acer-aspire-4930g Acer Aspire 4930G
+  medion       Medion Laptops
+  medion-md2   Medion MD2
+  targa-dig    Targa/MSI
+  targa-2ch-dig        Targs/MSI with 2-channel
+  laptop-eapd   3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE)
+  lenovo-101e  Lenovo 101E
+  lenovo-nb0763        Lenovo NB0763
+  lenovo-ms7195-dig Lenovo MS7195
+  lenovo-sky   Lenovo Sky
+  haier-w66    Haier W66
+  3stack-hp    HP machines with 3stack (Lucknow, Samba boards)
+  6stack-dell  Dell machines with 6stack (Inspiron 530)
+  mitac                Mitac 8252D
+  clevo-m720   Clevo M720 laptop series
+  fujitsu-pi2515 Fujitsu AMILO Pi2515
+  fujitsu-xa3530 Fujitsu AMILO XA3530
+  3stack-6ch-intel Intel DG33* boards
+  auto         auto-config reading BIOS (default)
+
+ALC861/660
+==========
+  3stack       3-jack
+  3stack-dig   3-jack with SPDIF I/O
+  6stack-dig   6-jack with SPDIF I/O
+  3stack-660   3-jack (for ALC660)
+  uniwill-m31  Uniwill M31 laptop
+  toshiba      Toshiba laptop support
+  asus         Asus laptop support
+  asus-laptop  ASUS F2/F3 laptops
+  auto         auto-config reading BIOS (default)
+
+ALC861VD/660VD
+==============
+  3stack       3-jack
+  3stack-dig   3-jack with SPDIF OUT
+  6stack-dig   6-jack with SPDIF OUT
+  3stack-660   3-jack (for ALC660VD)
+  3stack-660-digout 3-jack with SPDIF OUT (for ALC660VD)
+  lenovo       Lenovo 3000 C200
+  dallas       Dallas laptops
+  hp           HP TX1000
+  asus-v1s     ASUS V1Sn
+  auto         auto-config reading BIOS (default)
+
+CMI9880
+=======
+  minimal      3-jack in back
+  min_fp       3-jack in back, 2-jack in front
+  full         6-jack in back, 2-jack in front
+  full_dig     6-jack in back, 2-jack in front, SPDIF I/O
+  allout       5-jack in back, 2-jack in front, SPDIF out
+  auto         auto-config reading BIOS (default)
+
+AD1882 / AD1882A
+================
+  3stack       3-stack mode (default)
+  6stack       6-stack mode
+
+AD1884A / AD1883 / AD1984A / AD1984B
+====================================
+  desktop      3-stack desktop (default)
+  laptop       laptop with HP jack sensing
+  mobile       mobile devices with HP jack sensing
+  thinkpad     Lenovo Thinkpad X300
+
+AD1884
+======
+  N/A
+
+AD1981
+======
+  basic                3-jack (default)
+  hp           HP nx6320
+  thinkpad     Lenovo Thinkpad T60/X60/Z60
+  toshiba      Toshiba U205
+
+AD1983
+======
+  N/A
+
+AD1984
+======
+  basic                default configuration
+  thinkpad     Lenovo Thinkpad T61/X61
+  dell         Dell T3400
+
+AD1986A
+=======
+  6stack       6-jack, separate surrounds (default)
+  3stack       3-stack, shared surrounds
+  laptop       2-channel only (FSC V2060, Samsung M50)
+  laptop-eapd  2-channel with EAPD (ASUS A6J)
+  laptop-automute 2-channel with EAPD and HP-automute (Lenovo N100)
+  ultra                2-channel with EAPD (Samsung Ultra tablet PC)
+  samsung      2-channel with EAPD (Samsung R65)
+
+AD1988/AD1988B/AD1989A/AD1989B
+==============================
+  6stack       6-jack
+  6stack-dig   ditto with SPDIF
+  3stack       3-jack
+  3stack-dig   ditto with SPDIF
+  laptop       3-jack with hp-jack automute
+  laptop-dig   ditto with SPDIF
+  auto         auto-config reading BIOS (default)
+
+Conexant 5045
+=============
+  laptop-hpsense    Laptop with HP sense (old model laptop)
+  laptop-micsense   Laptop with Mic sense (old model fujitsu)
+  laptop-hpmicsense Laptop with HP and Mic senses
+  benq         Benq R55E
+  test         for testing/debugging purpose, almost all controls
+               can be adjusted.  Appearing only when compiled with
+               $CONFIG_SND_DEBUG=y
+
+Conexant 5047
+=============
+  laptop       Basic Laptop config 
+  laptop-hp    Laptop config for some HP models (subdevice 30A5)
+  laptop-eapd  Laptop config with EAPD support
+  test         for testing/debugging purpose, almost all controls
+               can be adjusted.  Appearing only when compiled with
+               $CONFIG_SND_DEBUG=y
+
+Conexant 5051
+=============
+  laptop       Basic Laptop config (default)
+  hp           HP Spartan laptop
+
+STAC9200
+========
+  ref          Reference board
+  dell-d21     Dell (unknown)
+  dell-d22     Dell (unknown)
+  dell-d23     Dell (unknown)
+  dell-m21     Dell Inspiron 630m, Dell Inspiron 640m
+  dell-m22     Dell Latitude D620, Dell Latitude D820
+  dell-m23     Dell XPS M1710, Dell Precision M90
+  dell-m24     Dell Latitude 120L
+  dell-m25     Dell Inspiron E1505n
+  dell-m26     Dell Inspiron 1501
+  dell-m27     Dell Inspiron E1705/9400
+  gateway      Gateway laptops with EAPD control
+  panasonic    Panasonic CF-74
+
+STAC9205/9254
+=============
+  ref          Reference board
+  dell-m42     Dell (unknown)
+  dell-m43     Dell Precision
+  dell-m44     Dell Inspiron
+
+STAC9220/9221
+=============
+  ref          Reference board
+  3stack       D945 3stack
+  5stack       D945 5stack + SPDIF
+  intel-mac-v1 Intel Mac Type 1
+  intel-mac-v2 Intel Mac Type 2
+  intel-mac-v3 Intel Mac Type 3
+  intel-mac-v4 Intel Mac Type 4
+  intel-mac-v5 Intel Mac Type 5
+  intel-mac-auto Intel Mac (detect type according to subsystem id)
+  macmini      Intel Mac Mini (equivalent with type 3)
+  macbook      Intel Mac Book (eq. type 5)
+  macbook-pro-v1 Intel Mac Book Pro 1st generation (eq. type 3)
+  macbook-pro  Intel Mac Book Pro 2nd generation (eq. type 3)
+  imac-intel   Intel iMac (eq. type 2)
+  imac-intel-20        Intel iMac (newer version) (eq. type 3)
+  dell-d81     Dell (unknown)
+  dell-d82     Dell (unknown)
+  dell-m81     Dell (unknown)
+  dell-m82     Dell XPS M1210
+
+STAC9202/9250/9251
+==================
+  ref          Reference board, base config
+  m2-2         Some Gateway MX series laptops
+  m6           Some Gateway NX series laptops
+  pa6          Gateway NX860 series
+
+STAC9227/9228/9229/927x
+=======================
+  ref          Reference board
+  ref-no-jd    Reference board without HP/Mic jack detection
+  3stack       D965 3stack
+  5stack       D965 5stack + SPDIF
+  dell-3stack  Dell Dimension E520
+  dell-bios    Fixes with Dell BIOS setup
+
+STAC92HD71B*
+============
+  ref          Reference board
+  dell-m4-1    Dell desktops
+  dell-m4-2    Dell desktops
+  dell-m4-3    Dell desktops
+
+STAC92HD73*
+===========
+  ref          Reference board
+  no-jd                BIOS setup but without jack-detection
+  dell-m6-amic Dell desktops/laptops with analog mics
+  dell-m6-dmic Dell desktops/laptops with digital mics
+  dell-m6      Dell desktops/laptops with both type of mics
+
+STAC92HD83*
+===========
+  ref          Reference board
+
+STAC9872
+========
+  vaio         Setup for VAIO FE550G/SZ110
+  vaio-ar Setup for VAIO AR
diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt
new file mode 100644 (file)
index 0000000..8d68fff
--- /dev/null
@@ -0,0 +1,577 @@
+MORE NOTES ON HD-AUDIO DRIVER
+=============================
+                                       Takashi Iwai <tiwai@suse.de>
+
+
+GENERAL
+-------
+
+HD-audio is the new standard on-board audio component on modern PCs
+after AC97.  Although Linux has been supporting HD-audio since long
+time ago, there are often problems with new machines.  A part of the
+problem is broken BIOS, and the rest is the driver implementation.
+This document explains the brief trouble-shooting and debugging
+methods for the        HD-audio hardware.
+
+The HD-audio component consists of two parts: the controller chip and 
+the codec chips on the HD-audio bus.  Linux provides a single driver
+for all controllers, snd-hda-intel.  Although the driver name contains
+a word of a well-known harware vendor, it's not specific to it but for
+all controller chips by other companies.  Since the HD-audio
+controllers are supposed to be compatible, the single snd-hda-driver
+should work in most cases.  But, not surprisingly, there are known
+bugs and issues specific to each controller type.  The snd-hda-intel
+driver has a bunch of workarounds for these as described below.
+
+A controller may have multiple codecs.  Usually you have one audio
+codec and optionally one modem codec.  In theory, there might be
+multiple audio codecs, e.g. for analog and digital outputs, and the
+driver might not work properly because of conflict of mixer elements.
+This should be fixed in future if such hardware really exists.
+
+The snd-hda-intel driver has several different codec parsers depending
+on the codec.  It has a generic parser as a fallback, but this
+functionality is fairly limited until now.  Instead of the generic
+parser, usually the codec-specific parser (coded in patch_*.c) is used
+for the codec-specific implementations.  The details about the
+codec-specific problems are explained in the later sections.
+
+If you are interested in the deep debugging of HD-audio, read the
+HD-audio specification at first.  The specification is found on
+Intel's web page, for example:
+
+- http://www.intel.com/standards/hdaudio/
+
+
+HD-AUDIO CONTROLLER
+-------------------
+
+DMA-Position Problem
+~~~~~~~~~~~~~~~~~~~~
+The most common problem of the controller is the inaccurate DMA
+pointer reporting.  The DMA pointer for playback and capture can be
+read in two ways, either via a LPIB register or via a position-buffer
+map.  As default the driver tries to read from the io-mapped
+position-buffer, and falls back to LPIB if the position-buffer appears
+dead.  However, this detection isn't perfect on some devices.  In such
+a case, you can change the default method via `position_fix` option.
+
+`position_fix=1` means to use LPIB method explicitly.
+`position_fix=2` means to use the position-buffer.  0 is the default
+value, the automatic check and fallback to LPIB as described in the
+above.  If you get a problem of repeated sounds, this option might
+help.
+
+In addition to that, every controller is known to be broken regarding
+the wake-up timing.  It wakes up a few samples before actually
+processing the data on the buffer.  This caused a lot of problems, for
+example, with ALSA dmix or JACK.  Since 2.6.27 kernel, the driver puts
+an artificial delay to the wake up timing.  This delay is controlled
+via `bdl_pos_adj` option. 
+
+When `bdl_pos_adj` is a negative value (as default), it's assigned to
+an appropriate value depending on the controller chip.  For Intel
+chips, it'd be 1 while it'd be 32 for others.  Usually this works.
+Only in case it doesn't work and you get warning messages, you should
+change this parameter to other values.
+
+
+Codec-Probing Problem
+~~~~~~~~~~~~~~~~~~~~~
+A less often but a more severe problem is the codec probing.  When
+BIOS reports the available codec slots wrongly, the driver gets
+confused and tries to access the non-existing codec slot.  This often
+results in the total screw-up, and destructs the further communication
+with the codec chips.  The symptom appears usually as error messages
+like:
+------------------------------------------------------------------------
+  hda_intel: azx_get_response timeout, switching to polling mode:
+        last cmd=0x12345678
+  hda_intel: azx_get_response timeout, switching to single_cmd mode:
+        last cmd=0x12345678
+------------------------------------------------------------------------
+
+The first line is a warning, and this is usually relatively harmless.
+It means that the codec response isn't notified via an IRQ.  The
+driver uses explicit polling method to read the response.  It gives
+very slight CPU overhead, but you'd unlikely notice it.
+
+The second line is, however, a fatal error.  If this happens, usually
+it means that something is really wrong.  Most likely you are
+accessing a non-existing codec slot.
+
+Thus, if the second error message appears, try to narrow the probed
+codec slots via `probe_mask` option.  It's a bitmask, and each bit
+corresponds to the codec slot.  For example, to probe only the first
+slot, pass `probe_mask=1`.  For the first and the third slots, pass
+`probe_mask=5` (where 5 = 1 | 4), and so on.
+
+Since 2.6.29 kernel, the driver has a more robust probing method, so
+this error might happen rarely, though.
+
+
+Interrupt Handling
+~~~~~~~~~~~~~~~~~~
+In rare but some cases, the interrupt isn't properly handled as
+default.  You would notice this by the DMA transfer error reported by
+ALSA PCM core, for example.  Using MSI might help in such a case.
+Pass `enable_msi=1` option for enabling MSI.
+
+
+HD-AUDIO CODEC
+--------------
+
+Model Option
+~~~~~~~~~~~~
+The most common problem regarding the HD-audio driver is the
+unsupported codec features or the mismatched device configuration.
+Most of codec-specific code has several preset models, either to
+override the BIOS setup or to provide more comprehensive features.
+
+The driver checks PCI SSID and looks through the static configuration
+table until any matching entry is found.  If you have a new machine,
+you may see a message like below:
+------------------------------------------------------------------------
+    hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...
+------------------------------------------------------------------------
+Even if you see such a message, DON'T PANIC.  Take a deep breath and
+keep your towel.  First of all, it's an informational message, no
+warning, no error.  This means that the PCI SSID of your device isn't
+listed in the known preset model (white-)list.  But, this doesn't mean
+that the driver is broken.  Many codec-drivers provide the automatic
+configuration mechanism based on the BIOS setup.
+
+The HD-audio codec has usually "pin" widgets, and BIOS sets the default
+configuration of each pin, which indicates the location, the
+connection type, the jack color, etc.  The HD-audio driver can guess
+the right connection judging from these default configuration values.
+However -- some codec-support codes, such as patch_analog.c, don't
+support the automatic probing (yet as of 2.6.28).  And, BIOS is often,
+yes, pretty often broken.  It sets up wrong values and screws up the
+driver.
+
+The preset model is provided basically to overcome such a situation.
+When the matching preset model is found in the white-list, the driver
+assumes the static configuration of that preset and builds the mixer
+elements and PCM streams based on the static information.  Thus, if
+you have a newer machine with a slightly different PCI SSID from the
+existing one, you may have a good chance to re-use the same model.
+You can pass the `model` option to specify the preset model instead of
+PCI SSID look-up.
+
+What `model` option values are available depends on the codec chip.
+Check your codec chip from the codec proc file (see "Codec Proc-File"
+section below).  It will show the vendor/product name of your codec
+chip.  Then, see Documentation/sound/alsa/HD-Audio-Modelstxt file,
+the section of HD-audio driver.  You can find a list of codecs
+and `model` options belonging to each codec.  For example, for Realtek
+ALC262 codec chip, pass `model=ultra` for devices that are compatible
+with Samsung Q1 Ultra.
+
+Thus, the first thing you can do for any brand-new, unsupported and
+non-working HD-audio hardware is to check HD-audio codec and several
+different `model` option values.  If you have a luck, some of them
+might suit with your device well.
+
+Some codecs such as ALC880 have a special model option `model=test`.
+This configures the driver to provide as many mixer controls as
+possible for every single pin feature except for the unsolicited
+events (and maybe some other specials).  Adjust each mixer element and
+try the I/O in the way of trial-and-error until figuring out the whole
+I/O pin mappings.
+
+Note that `model=generic` has a special meaning.  It means to use the
+generic parser regardless of the codec.  Usually the codec-specific
+parser is much better than the generic parser (as now).  Thus this
+option is more about the debugging purpose.
+
+
+Speaker and Headphone Output
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+One of the most frequent (and obvious) bugs with HD-audio is the
+silent output from either or both of a built-in speaker and a
+headphone jack.  In general, you should try a headphone output at
+first.  A speaker output often requires more additional controls like
+the external amplifier bits.  Thus a headphone output has a slightly
+better chance.
+
+Before making a bug report, double-check whether the mixer is set up
+correctly.  The recent version of snd-hda-intel driver provides mostly
+"Master" volume control as well as "Front" volume (where Front
+indicates the front-channels).  In addition, there can be individual
+"Headphone" and "Speaker" controls.
+
+Ditto for the speaker output.  There can be "External Amplifier"
+switch on some codecs.  Turn on this if present.
+
+Another related problem is the automatic mute of speaker output by
+headphone plugging.  This feature is implemented in most cases, but
+not on every preset model or codec-support code.
+
+In anyway, try a different model option if you have such a problem.
+Some other models may match better and give you more matching
+functionality.  If none of the available models works, send a bug
+report.  See the bug report section for details.
+
+If you are masochistic enough to debug the driver problem, note the
+following:
+
+- The speaker (and the headphone, too) output often requires the
+  external amplifier.  This can be set usually via EAPD verb or a
+  certain GPIO.  If the codec pin supports EAPD, you have a better
+  chance via SET_EAPD_BTL verb (0x70c).  On others, GPIO pin (mostly
+  it's either GPIO0 or GPIO1) may turn on/off EAPD.
+- Some Realtek codecs require special vendor-specific coefficients to
+  turn on the amplifier.  See patch_realtek.c.
+- IDT codecs may have extra power-enable/disable controls on each
+  analog pin.  See patch_sigmatel.c.
+- Very rare but some devices don't accept the pin-detection verb until
+  triggered.  Issuing GET_PIN_SENSE verb (0xf09) may result in the
+  codec-communication stall.  Some examples are found in
+  patch_realtek.c.
+
+
+Capture Problems
+~~~~~~~~~~~~~~~~
+The capture problems are often because of missing setups of mixers.
+Thus, before submitting a bug report, make sure that you set up the
+mixer correctly.  For example, both "Capture Volume" and "Capture
+Switch" have to be set properly in addition to the right "Capture
+Source" or "Input Source" selection.  Some devices have "Mic Boost"
+volume or switch.
+
+When the PCM device is opened via "default" PCM (without pulse-audio
+plugin), you'll likely have "Digital Capture Volume" control as well.
+This is provided for the extra gain/attenuation of the signal in
+software, especially for the inputs without the hardware volume
+control such as digital microphones.  Unless really needed, this
+should be set to exactly 50%, corresponding to 0dB -- neither extra
+gain nor attenuation.  When you use "hw" PCM, i.e., a raw access PCM,
+this control will have no influence, though.
+
+It's known that some codecs / devices have fairly bad analog circuits,
+and the recorded sound contains a certain DC-offset.  This is no bug
+of the driver.
+
+Most of modern laptops have no analog CD-input connection.  Thus, the
+recording from CD input won't work in many cases although the driver
+provides it as the capture source.  Use CDDA instead.
+
+The automatic switching of the built-in and external mic per plugging
+is implemented on some codec models but not on every model.  Partly
+because of my laziness but mostly lack of testers.  Feel free to
+submit the improvement patch to the author.
+
+
+Direct Debugging
+~~~~~~~~~~~~~~~~
+If no model option gives you a better result, and you are a tough guy
+to fight against evil, try debugging via hitting the raw HD-audio
+codec verbs to the device.  Some tools are available: hda-emu and
+hda-analyzer.  The detailed description is found in the sections
+below.  You'd need to enable hwdep for using these tools.  See "Kernel
+Configuration" section.
+
+
+OTHER ISSUES
+------------
+
+Kernel Configuration
+~~~~~~~~~~~~~~~~~~~~
+In general, I recommend you to enable the sound debug option,
+`CONFIG_SND_DEBUG=y`, no matter whether you are debugging or not.
+This enables snd_printd() macro and others, and you'll get additional
+kernel messages at probing.
+
+In addition, you can enable `CONFIG_SND_DEBUG_VERBOSE=y`.  But this
+will give you far more messages.  Thus turn this on only when you are
+sure to want it.
+
+Don't forget to turn on the appropriate `CONFIG_SND_HDA_CODEC_*`
+options.  Note that each of them corresponds to the codec chip, not
+the controller chip.  Thus, even if lspci shows the Nvidia controller,
+you may need to choose the option for other vendors.  If you are
+unsure, just select all yes.
+
+`CONFIG_SND_HDA_HWDEP` is a useful option for debugging the driver.
+When this is enabled, the driver creates hardware-dependent devices
+(one per each codec), and you have a raw access to the device via
+these device files.  For example, `hwC0D2` will be created for the
+codec slot #2 of the first card (#0).  For debug-tools such as
+hda-verb and hda-analyzer, the hwdep device has to be enabled.
+Thus, it'd be better to turn this on always.
+
+`CONFIG_SND_HDA_RECONFIG` is a new option, and this depends on the
+hwdep option above.  When enabled, you'll have some sysfs files under
+the corresponding hwdep directory.  See "HD-audio reconfiguration"
+section below.
+
+`CONFIG_SND_HDA_POWER_SAVE` option enables the power-saving feature.
+See "Power-saving" section below.
+
+
+Codec Proc-File
+~~~~~~~~~~~~~~~
+The codec proc-file is a treasure-chest for debugging HD-audio.
+It shows most of useful information of each codec widget.
+
+The proc file is located in /proc/asound/card*/codec#*, one file per
+each codec slot.  You can know the codec vendor, product id and
+names, the type of each widget, capabilities and so on.
+This file, however, doesn't show the jack sensing state, so far.  This
+is because the jack-sensing might be depending on the trigger state.
+
+This file will be picked up by the debug tools, and also it can be fed
+to the emulator as the primary codec information.  See the debug tools
+section below.
+
+This proc file can be also used to check whether the generic parser is
+used.  When the generic parser is used, the vendor/product ID name
+will appear as "Realtek ID 0262", instead of "Realtek ALC262".
+
+
+HD-Audio Reconfiguration
+~~~~~~~~~~~~~~~~~~~~~~~~
+This is an experimental feature to allow you re-configure the HD-audio
+codec dynamically without reloading the driver.  The following sysfs
+files are available under each codec-hwdep device directory (e.g. 
+/sys/class/sound/hwC0D0):
+
+vendor_id::
+  Shows the 32bit codec vendor-id hex number.  You can change the
+  vendor-id value by writing to this file.
+subsystem_id::
+  Shows the 32bit codec subsystem-id hex number.  You can change the
+  subsystem-id value by writing to this file.
+revision_id::
+  Shows the 32bit codec revision-id hex number.  You can change the
+  revision-id value by writing to this file.
+afg::
+  Shows the AFG ID.  This is read-only.
+mfg::
+  Shows the MFG ID.  This is read-only.
+name::
+  Shows the codec name string.  Can be changed by writing to this
+  file.
+modelname::
+  Shows the currently set `model` option.  Can be changed by writing
+  to this file.
+init_verbs::
+  The extra verbs to execute at initialization.  You can add a verb by
+  writing to this file.  Pass tree numbers, nid, verb and parameter.
+hints::
+  Shows hint strings for codec parsers for any use.  Right now it's
+  not used.
+reconfig::
+  Triggers the codec re-configuration.  When any value is written to
+  this file, the driver re-initialize and parses the codec tree
+  again.  All the changes done by the sysfs entries above are taken
+  into account.
+clear::
+  Resets the codec, removes the mixer elements and PCM stuff of the
+  specified codec, and clear all init verbs and hints.
+
+
+Power-Saving
+~~~~~~~~~~~~
+The power-saving is a kind of auto-suspend of the device.  When the
+device is inactive for a certain time, the device is automatically
+turned off to save the power.  The time to go down is specified via
+`power_save` module option, and this option can be changed dynamically
+via sysfs.
+
+The power-saving won't work when the analog loopback is enabled on
+some codecs.  Make sure that you mute all unneeded signal routes when
+you want the power-saving.
+
+The power-saving feature might cause audible click noises at each
+power-down/up depending on the device.  Some of them might be
+solvable, but some are hard, I'm afraid.  Some distros such as
+openSUSE enables the power-saving feature automatically when the power
+cable is unplugged.  Thus, if you hear noises, suspect first the
+power-saving.  See /sys/module/snd_hda_intel/parameters/power_save to
+check the current value.  If it's non-zero, the feature is turned on.
+
+
+Development Tree
+~~~~~~~~~~~~~~~~
+The latest development codes for HD-audio are found on sound git tree:
+
+- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6.git
+
+The master branch or for-next branches can be used as the main
+development branches in general while the HD-audio specific patches
+are committed in topic/hda branch.
+
+If you are using the latest Linus tree, it'd be better to pull the
+above GIT tree onto it.  If you are using the older kernels, an easy
+way to try the latest ALSA code is to build from the snapshot
+tarball.  There are daily tarballs and the latest snapshot tarball.
+All can be built just like normal alsa-driver release packages, that
+is, installed via the usual spells: configure, make and make
+install(-modules).  See INSTALL in the package.  The snapshot tarballs
+are found at:
+
+- ftp://ftp.kernel.org/pub/linux/kernel/people/tiwai/snapshot/
+
+
+Sending a Bug Report
+~~~~~~~~~~~~~~~~~~~~
+If any model or module options don't work for your device, it's time
+to send a bug report to the developers.  Give the following in your
+bug report:
+
+- Hardware vendor, product and model names
+- Kernel version (and ALSA-driver version if you built externally)
+- `alsa-info.sh` output; run with `--no-upload` option.  See the
+  section below about alsa-info
+
+If it's a regression, at best, send alsa-info outputs of both working
+and non-working kernels.  This is really helpful because we can
+compare the codec registers directly.
+
+Send a bug report either the followings:
+
+kernel-bugzilla::
+  http://bugme.linux-foundation.org/
+alsa-devel ML::
+  alsa-devel@alsa-project.org
+
+
+DEBUG TOOLS
+-----------
+
+This section describes some tools available for debugging HD-audio
+problems.
+
+alsa-info
+~~~~~~~~~
+The script `alsa-info.sh` is a very useful tool to gather the audio
+device information.  You can fetch the latest version from:
+
+- http://www.alsa-project.org/alsa-info.sh
+
+Run this script as root, and it will gather the important information
+such as the module lists, module parameters, proc file contents
+including the codec proc files, mixer outputs and the control
+elements.  As default, it will store the information onto a web server
+on alsa-project.org.  But, if you send a bug report, it'd be better to
+run with `--no-upload` option, and attach the generated file.
+
+There are some other useful options.  See `--help` option output for
+details.
+
+
+hda-verb
+~~~~~~~~
+hda-verb is a tiny program that allows you to access the HD-audio
+codec directly.  You can execute a raw HD-audio codec verb with this.
+This program accesses the hwdep device, thus you need to enable the
+kernel config `CONFIG_SND_HDA_HWDEP=y` beforehand.
+
+The hda-verb program takes four arguments: the hwdep device file, the
+widget NID, the verb and the parameter.  When you access to the codec
+on the slot 2 of the card 0, pass /dev/snd/hwC0D2 to the first
+argument, typically.  (However, the real path name depends on the
+system.)
+
+The second parameter is the widget number-id to access.  The third
+parameter can be either a hex/digit number or a string corresponding
+to a verb.  Similarly, the last parameter is the value to write, or
+can be a string for the parameter type.
+
+------------------------------------------------------------------------
+  % hda-verb /dev/snd/hwC0D0 0x12 0x701 2
+  nid = 0x12, verb = 0x701, param = 0x2
+  value = 0x0
+
+  % hda-verb /dev/snd/hwC0D0 0x0 PARAMETERS VENDOR_ID
+  nid = 0x0, verb = 0xf00, param = 0x0
+  value = 0x10ec0262
+
+  % hda-verb /dev/snd/hwC0D0 2 set_a 0xb080
+  nid = 0x2, verb = 0x300, param = 0xb080
+  value = 0x0
+------------------------------------------------------------------------
+
+Although you can issue any verbs with this program, the driver state
+won't be always updated.  For example, the volume values are usually
+cached in the driver, and thus changing the widget amp value directly
+via hda-verb won't change the mixer value.
+
+The hda-verb program is found in the ftp directory:
+
+- ftp://ftp.kernel.org/pub/linux/kernel/people/tiwai/misc/
+
+Also a git repository is available:
+
+- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-verb.git
+
+See README file in the tarball for more details about hda-verb
+program.
+
+
+hda-analyzer
+~~~~~~~~~~~~
+hda-analyzer provides a graphical interface to access the raw HD-audio
+control, based on pyGTK2 binding.  It's a more powerful version of
+hda-verb.  The program gives you an easy-to-use GUI stuff for showing
+the widget information and adjusting the amp values, as well as the
+proc-compatible output.
+
+The hda-analyzer is a part of alsa.git repository in
+alsa-project.org:
+
+- http://git.alsa-project.org/?p=alsa.git;a=tree;f=hda-analyzer
+
+
+Codecgraph
+~~~~~~~~~~
+Codecgraph is a utility program to generate a graph and visualizes the
+codec-node connection of a codec chip.  It's especially useful when
+you analyze or debug a codec without a proper datasheet.  The program
+parses the given codec proc file and converts to SVG via graphiz
+program.
+
+The tarball and GIT trees are found in the web page at:
+
+- http://helllabs.org/codecgraph/
+
+
+hda-emu
+~~~~~~~
+hda-emu is an HD-audio emulator.  The main purpose of this program is
+to debug an HD-audio codec without the real hardware.  Thus, it
+doesn't emulate the behavior with the real audio I/O, but it just
+dumps the codec register changes and the ALSA-driver internal changes
+at probing and operating the HD-audio driver.
+
+The program requires a codec proc-file to simulate.  Get a proc file
+for the target codec beforehand, or pick up an example codec from the
+codec proc collections in the tarball.  Then, run the program with the
+proc file, and the hda-emu program will start parsing the codec file
+and simulates the HD-audio driver:
+
+------------------------------------------------------------------------
+  % hda-emu codecs/stac9200-dell-d820-laptop
+  # Parsing..
+  hda_codec: Unknown model for STAC9200, using BIOS defaults
+  hda_codec: pin nid 08 bios pin config 40c003fa
+  ....
+------------------------------------------------------------------------
+
+The program gives you only a very dumb command-line interface.  You
+can get a proc-file dump at the current state, get a list of control
+(mixer) elements, set/get the control element value, simulate the PCM
+operation, the jack plugging simulation, etc.
+
+The package is found in:
+
+- ftp://ftp.kernel.org/pub/linux/kernel/people/tiwai/misc/
+
+A git repository is available:
+
+- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-emu.git
+
+See README file in the tarball for more details about hda-emu
+program.
index f738b29..bba2dbb 100644 (file)
@@ -153,6 +153,16 @@ card*/codec#*
        Shows the general codec information and the attribute of each
        widget node.
 
+card*/eld#*
+       Available for HDMI or DisplayPort interfaces.
+       Shows ELD(EDID Like Data) info retrieved from the attached HDMI sink,
+       and describes its audio capabilities and configurations.
+
+       Some ELD fields may be modified by doing `echo name hex_value > eld#*`.
+       Only do this if you are sure the HDMI sink provided value is wrong.
+       And if that makes your HDMI audio work, please report to us so that we
+       can fix it in future kernel releases.
+
 
 Sequencer Information
 ---------------------
index f370e7d..bab7711 100644 (file)
@@ -9,7 +9,7 @@ the audio subsystem with the kernel as a platform device and is represented by
 the following struct:-
 
 /* SoC machine */
-struct snd_soc_machine {
+struct snd_soc_card {
        char *name;
 
        int (*probe)(struct platform_device *pdev);
@@ -67,10 +67,10 @@ static struct snd_soc_dai_link corgi_dai = {
        .ops = &corgi_ops,
 };
 
-struct snd_soc_machine then sets up the machine with it's DAIs. e.g.
+struct snd_soc_card then sets up the machine with it's DAIs. e.g.
 
 /* corgi audio machine driver */
-static struct snd_soc_machine snd_soc_machine_corgi = {
+static struct snd_soc_card snd_soc_corgi = {
        .name = "Corgi",
        .dai_link = &corgi_dai,
        .num_links = 1,
@@ -90,7 +90,7 @@ static struct wm8731_setup_data corgi_wm8731_setup = {
 
 /* corgi audio subsystem */
 static struct snd_soc_device corgi_snd_devdata = {
-       .machine = &snd_soc_machine_corgi,
+       .machine = &snd_soc_corgi,
        .platform = &pxa2xx_soc_platform,
        .codec_dev = &soc_codec_dev_wm8731,
        .codec_data = &corgi_wm8731_setup,
index fbc8fa5..7010257 100644 (file)
@@ -3977,7 +3977,7 @@ M:        tiwai@suse.de
 L:     alsa-devel@alsa-project.org (subscribers-only)
 S:     Maintained
 
-SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT
+SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC)
 P:     Liam Girdwood
 M:     lrg@slimlogic.co.uk
 P:     Mark Brown
index 4b18cd9..6ff8886 100644 (file)
@@ -19,15 +19,18 @@ void foo(void)
        BLANK();
 
         DEFINE(TASK_BLOCKED, offsetof(struct task_struct, blocked));
-        DEFINE(TASK_UID, offsetof(struct task_struct, uid));
-        DEFINE(TASK_EUID, offsetof(struct task_struct, euid));
-        DEFINE(TASK_GID, offsetof(struct task_struct, gid));
-        DEFINE(TASK_EGID, offsetof(struct task_struct, egid));
+        DEFINE(TASK_CRED, offsetof(struct task_struct, cred));
         DEFINE(TASK_REAL_PARENT, offsetof(struct task_struct, real_parent));
         DEFINE(TASK_GROUP_LEADER, offsetof(struct task_struct, group_leader));
         DEFINE(TASK_TGID, offsetof(struct task_struct, tgid));
         BLANK();
 
+        DEFINE(CRED_UID,  offsetof(struct cred, uid));
+        DEFINE(CRED_EUID, offsetof(struct cred, euid));
+        DEFINE(CRED_GID,  offsetof(struct cred, gid));
+        DEFINE(CRED_EGID, offsetof(struct cred, egid));
+        BLANK();
+
        DEFINE(SIZEOF_PT_REGS, sizeof(struct pt_regs));
        DEFINE(PT_PTRACED, PT_PTRACED);
        DEFINE(CLONE_VM, CLONE_VM);
index 5fc61e2..f77345b 100644 (file)
@@ -850,8 +850,9 @@ osf_getpriority:
 sys_getxuid:
        .prologue 0
        ldq     $2, TI_TASK($8)
-       ldl     $0, TASK_UID($2)
-       ldl     $1, TASK_EUID($2)
+       ldq     $3, TASK_CRED($2)
+       ldl     $0, CRED_UID($3)
+       ldl     $1, CRED_EUID($3)
        stq     $1, 80($sp)
        ret
 .end sys_getxuid
@@ -862,8 +863,9 @@ sys_getxuid:
 sys_getxgid:
        .prologue 0
        ldq     $2, TI_TASK($8)
-       ldl     $0, TASK_GID($2)
-       ldl     $1, TASK_EGID($2)
+       ldq     $3, TASK_CRED($2)
+       ldl     $0, CRED_GID($3)
+       ldl     $1, CRED_EGID($3)
        stq     $1, 80($sp)
        ret
 .end sys_getxgid
diff --git a/arch/arm/mach-pxa/include/mach/palmasoc.h b/arch/arm/mach-pxa/include/mach/palmasoc.h
new file mode 100644 (file)
index 0000000..6c4b1f7
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _INCLUDE_PALMASOC_H_
+#define _INCLUDE_PALMASOC_H_
+struct palm27x_asoc_info {
+       int     jack_gpio;
+};
+
+#ifdef CONFIG_SND_PXA2XX_SOC_PALM27X
+void __init palm27x_asoc_set_pdata(struct palm27x_asoc_info *data);
+#else
+static inline void palm27x_asoc_set_pdata(struct palm27x_asoc_info *data) {}
+#endif
+
+#endif
index 5e92ae0..16ef61a 100644 (file)
@@ -1767,25 +1767,24 @@ groups16_from_user(struct group_info *group_info, short __user *grouplist)
 asmlinkage long
 sys32_getgroups16 (int gidsetsize, short __user *grouplist)
 {
+       const struct cred *cred = current_cred();
        int i;
 
        if (gidsetsize < 0)
                return -EINVAL;
 
-       get_group_info(current->group_info);
-       i = current->group_info->ngroups;
+       i = cred->group_info->ngroups;
        if (gidsetsize) {
                if (i > gidsetsize) {
                        i = -EINVAL;
                        goto out;
                }
-               if (groups16_to_user(grouplist, current->group_info)) {
+               if (groups16_to_user(grouplist, cred->group_info)) {
                        i = -EFAULT;
                        goto out;
                }
        }
 out:
-       put_group_info(current->group_info);
        return i;
 }
 
index fab1d21..f94aaa8 100644 (file)
@@ -158,7 +158,7 @@ mca_handler_bh(unsigned long paddr, void *iip, unsigned long ipsr)
        ia64_mlogbuf_dump();
        printk(KERN_ERR "OS_MCA: process [cpu %d, pid: %d, uid: %d, "
                "iip: %p, psr: 0x%lx,paddr: 0x%lx](%s) encounters MCA.\n",
-               raw_smp_processor_id(), current->pid, current->uid,
+              raw_smp_processor_id(), current->pid, current_uid(),
                iip, ipsr, paddr, current->comm);
 
        spin_lock(&mca_bh_lock);
index 6543a55..0e49975 100644 (file)
@@ -2220,8 +2220,8 @@ pfm_alloc_file(pfm_context_t *ctx)
        DPRINT(("new inode ino=%ld @%p\n", inode->i_ino, inode));
 
        inode->i_mode = S_IFCHR|S_IRUGO;
-       inode->i_uid  = current->fsuid;
-       inode->i_gid  = current->fsgid;
+       inode->i_uid  = current_fsuid();
+       inode->i_gid  = current_fsgid();
 
        sprintf(name, "[%lu]", inode->i_ino);
        this.name = name;
@@ -2399,22 +2399,33 @@ error_kmem:
 static int
 pfm_bad_permissions(struct task_struct *task)
 {
+       const struct cred *tcred;
+       uid_t uid = current_uid();
+       gid_t gid = current_gid();
+       int ret;
+
+       rcu_read_lock();
+       tcred = __task_cred(task);
+
        /* inspired by ptrace_attach() */
        DPRINT(("cur: uid=%d gid=%d task: euid=%d suid=%d uid=%d egid=%d sgid=%d\n",
-               current->uid,
-               current->gid,
-               task->euid,
-               task->suid,
-               task->uid,
-               task->egid,
-               task->sgid));
-
-       return ((current->uid != task->euid)
-           || (current->uid != task->suid)
-           || (current->uid != task->uid)
-           || (current->gid != task->egid)
-           || (current->gid != task->sgid)
-           || (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE);
+               uid,
+               gid,
+               tcred->euid,
+               tcred->suid,
+               tcred->uid,
+               tcred->egid,
+               tcred->sgid));
+
+       ret = ((uid != tcred->euid)
+              || (uid != tcred->suid)
+              || (uid != tcred->uid)
+              || (gid != tcred->egid)
+              || (gid != tcred->sgid)
+              || (gid != tcred->gid)) && !capable(CAP_SYS_PTRACE);
+
+       rcu_read_unlock();
+       return ret;
 }
 
 static int
index e12500a..e1821ca 100644 (file)
@@ -229,7 +229,7 @@ ia64_rt_sigreturn (struct sigscratch *scr)
        si.si_errno = 0;
        si.si_code = SI_KERNEL;
        si.si_pid = task_pid_vnr(current);
-       si.si_uid = current->uid;
+       si.si_uid = current_uid();
        si.si_addr = sc;
        force_sig_info(SIGSEGV, &si, current);
        return retval;
@@ -326,7 +326,7 @@ force_sigsegv_info (int sig, void __user *addr)
        si.si_errno = 0;
        si.si_code = SI_KERNEL;
        si.si_pid = task_pid_vnr(current);
-       si.si_uid = current->uid;
+       si.si_uid = current_uid();
        si.si_addr = addr;
        force_sig_info(SIGSEGV, &si, current);
        return 0;
index b0591ae..fd6e512 100644 (file)
@@ -174,8 +174,8 @@ static unsigned int translate_open_flags(int flags)
 
 static void sp_setfsuidgid( uid_t uid, gid_t gid)
 {
-       current->fsuid = uid;
-       current->fsgid = gid;
+       current->cred->fsuid = uid;
+       current->cred->fsgid = gid;
 
        key_fsuid_changed(current);
        key_fsgid_changed(current);
index dc9eb72..5e77a3a 100644 (file)
@@ -51,6 +51,7 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len,
        int retval;
        struct task_struct *p;
        struct thread_info *ti;
+       uid_t euid;
 
        if (len < sizeof(new_mask))
                return -EINVAL;
@@ -76,9 +77,9 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len,
         */
        get_task_struct(p);
 
+       euid = current_euid();
        retval = -EPERM;
-       if ((current->euid != p->euid) && (current->euid != p->uid) &&
-                       !capable(CAP_SYS_NICE)) {
+       if (euid != p->euid && euid != p->uid && !capable(CAP_SYS_NICE)) {
                read_unlock(&tasklist_lock);
                goto out_unlock;
        }
index a1b3da6..010b27e 100644 (file)
@@ -1085,8 +1085,8 @@ static int vpe_open(struct inode *inode, struct file *filp)
        v->load_addr = NULL;
        v->len = 0;
 
-       v->uid = filp->f_uid;
-       v->gid = filp->f_gid;
+       v->uid = filp->f_cred->fsuid;
+       v->gid = filp->f_cred->fsgid;
 
 #ifdef CONFIG_MIPS_APSP_KSPD
        /* get kspd to tell us when a syscall_exit happens */
index 06213d1..f825442 100644 (file)
@@ -182,7 +182,7 @@ give_sigsegv:
        si.si_errno = 0;
        si.si_code = SI_KERNEL;
        si.si_pid = task_pid_vnr(current);
-       si.si_uid = current->uid;
+       si.si_uid = current_uid();
        si.si_addr = &frame->uc;
        force_sig_info(SIGSEGV, &si, current);
        return;
index 565b7a2..8660986 100644 (file)
@@ -339,7 +339,7 @@ bad_area_nosemaphore:
            && printk_ratelimit())
                printk(KERN_CRIT "kernel tried to execute NX-protected"
                       " page (%lx) - exploit attempt? (uid: %d)\n",
-                      address, current->uid);
+                      address, current_uid());
 
        return SIGSEGV;
 
index cb85d23..6296bfd 100644 (file)
@@ -95,8 +95,8 @@ spufs_new_inode(struct super_block *sb, int mode)
                goto out;
 
        inode->i_mode = mode;
-       inode->i_uid = current->fsuid;
-       inode->i_gid = current->fsgid;
+       inode->i_uid = current_fsuid();
+       inode->i_gid = current_fsgid();
        inode->i_blocks = 0;
        inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 out:
@@ -323,7 +323,7 @@ static int spufs_context_open(struct dentry *dentry, struct vfsmount *mnt)
                goto out;
        }
 
-       filp = dentry_open(dentry, mnt, O_RDONLY);
+       filp = dentry_open(dentry, mnt, O_RDONLY, current_cred());
        if (IS_ERR(filp)) {
                put_unused_fd(ret);
                ret = PTR_ERR(filp);
@@ -562,7 +562,7 @@ static int spufs_gang_open(struct dentry *dentry, struct vfsmount *mnt)
                goto out;
        }
 
-       filp = dentry_open(dentry, mnt, O_RDONLY);
+       filp = dentry_open(dentry, mnt, O_RDONLY, current_cred());
        if (IS_ERR(filp)) {
                put_unused_fd(ret);
                ret = PTR_ERR(filp);
index 3631380..8aadcd7 100644 (file)
@@ -280,8 +280,8 @@ static int hypfs_fill_super(struct super_block *sb, void *data, int silent)
        if (!sbi)
                return -ENOMEM;
        mutex_init(&sbi->lock);
-       sbi->uid = current->uid;
-       sbi->gid = current->gid;
+       sbi->uid = current_uid();
+       sbi->gid = current_gid();
        sb->s_fs_info = sbi;
        sb->s_blocksize = PAGE_CACHE_SIZE;
        sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
index 4646382..6cc87d8 100644 (file)
@@ -148,9 +148,9 @@ asmlinkage long sys32_getresuid16(u16 __user *ruid, u16 __user *euid, u16 __user
 {
        int retval;
 
-       if (!(retval = put_user(high2lowuid(current->uid), ruid)) &&
-           !(retval = put_user(high2lowuid(current->euid), euid)))
-               retval = put_user(high2lowuid(current->suid), suid);
+       if (!(retval = put_user(high2lowuid(current->cred->uid), ruid)) &&
+           !(retval = put_user(high2lowuid(current->cred->euid), euid)))
+               retval = put_user(high2lowuid(current->cred->suid), suid);
 
        return retval;
 }
@@ -165,9 +165,9 @@ asmlinkage long sys32_getresgid16(u16 __user *rgid, u16 __user *egid, u16 __user
 {
        int retval;
 
-       if (!(retval = put_user(high2lowgid(current->gid), rgid)) &&
-           !(retval = put_user(high2lowgid(current->egid), egid)))
-               retval = put_user(high2lowgid(current->sgid), sgid);
+       if (!(retval = put_user(high2lowgid(current->cred->gid), rgid)) &&
+           !(retval = put_user(high2lowgid(current->cred->egid), egid)))
+               retval = put_user(high2lowgid(current->cred->sgid), sgid);
 
        return retval;
 }
@@ -217,20 +217,20 @@ asmlinkage long sys32_getgroups16(int gidsetsize, u16 __user *grouplist)
        if (gidsetsize < 0)
                return -EINVAL;
 
-       get_group_info(current->group_info);
-       i = current->group_info->ngroups;
+       get_group_info(current->cred->group_info);
+       i = current->cred->group_info->ngroups;
        if (gidsetsize) {
                if (i > gidsetsize) {
                        i = -EINVAL;
                        goto out;
                }
-               if (groups16_to_user(grouplist, current->group_info)) {
+               if (groups16_to_user(grouplist, current->cred->group_info)) {
                        i = -EFAULT;
                        goto out;
                }
        }
 out:
-       put_group_info(current->group_info);
+       put_group_info(current->cred->group_info);
        return i;
 }
 
@@ -261,22 +261,22 @@ asmlinkage long sys32_setgroups16(int gidsetsize, u16 __user *grouplist)
 
 asmlinkage long sys32_getuid16(void)
 {
-       return high2lowuid(current->uid);
+       return high2lowuid(current->cred->uid);
 }
 
 asmlinkage long sys32_geteuid16(void)
 {
-       return high2lowuid(current->euid);
+       return high2lowuid(current->cred->euid);
 }
 
 asmlinkage long sys32_getgid16(void)
 {
-       return high2lowgid(current->gid);
+       return high2lowgid(current->cred->gid);
 }
 
 asmlinkage long sys32_getegid16(void)
 {
-       return high2lowgid(current->egid);
+       return high2lowgid(current->cred->egid);
 }
 
 /*
index 5c9cbfc..f32a519 100644 (file)
@@ -13,6 +13,7 @@ config SUPERH
        select HAVE_OPROFILE
        select HAVE_GENERIC_DMA_COHERENT
        select HAVE_IOREMAP_PROT if MMU
+       select HAVE_ARCH_TRACEHOOK
        help
          The SuperH is a RISC processor targeted for use in embedded systems
          and consumer electronics; it was also used in the Sega Dreamcast
@@ -23,8 +24,10 @@ config SUPERH32
        def_bool !SUPERH64
        select HAVE_KPROBES
        select HAVE_KRETPROBES
-       select HAVE_ARCH_TRACEHOOK
        select HAVE_FUNCTION_TRACER
+       select HAVE_FTRACE_MCOUNT_RECORD
+       select HAVE_DYNAMIC_FTRACE
+       select HAVE_ARCH_KGDB
 
 config SUPERH64
        def_bool y if CPU_SH5
@@ -55,8 +58,6 @@ config GENERIC_HARDIRQS
 
 config GENERIC_HARDIRQS_NO__DO_IRQ
        def_bool y
-       depends on SUPERH32 && (!SH_DREAMCAST && !SH_SH4202_MICRODEV && \
-                               !SH_7751_SYSTEMH && !HD64461)
 
 config GENERIC_IRQ_PROBE
        def_bool y
@@ -85,10 +86,17 @@ config GENERIC_LOCKBREAK
 
 config SYS_SUPPORTS_PM
        bool
+       depends on !SMP
+
+config ARCH_SUSPEND_POSSIBLE
+       def_bool n
+
+config ARCH_HIBERNATION_POSSIBLE
+       def_bool n
 
 config SYS_SUPPORTS_APM_EMULATION
        bool
-       select SYS_SUPPORTS_PM
+       select ARCH_SUSPEND_POSSIBLE
 
 config SYS_SUPPORTS_SMP
        bool
@@ -183,6 +191,11 @@ config CPU_SUBTYPE_SH7619
 
 # SH-2A Processor Support
 
+config CPU_SUBTYPE_SH7201
+       bool "Support SH7201 processor"
+       select CPU_SH2A
+       select CPU_HAS_FPU
 config CPU_SUBTYPE_SH7203
        bool "Support SH7203 processor"
        select CPU_SH2A
@@ -456,8 +469,12 @@ config SH_CPU_FREQ
        depends on CPU_FREQ
        select CPU_FREQ_TABLE
        help
-         This adds the cpufreq driver for SuperH. At present, only
-         the SH-4 is supported.
+         This adds the cpufreq driver for SuperH. Any CPU that supports
+         clock rate rounding through the clock framework can use this
+         driver. While it will make the kernel slightly larger, this is
+         harmless for CPUs that don't support rate rounding. The driver
+         will also generate a notice in the boot log before disabling
+         itself if the CPU in question is not capable of rate rounding.
 
          For details, take a look at <file:Documentation/cpu-freq>.
 
@@ -469,9 +486,6 @@ source "arch/sh/drivers/Kconfig"
 
 endmenu
 
-config ISA_DMA_API
-       bool
-
 menu "Kernel features"
 
 source kernel/Kconfig.hz
@@ -688,49 +702,6 @@ config MAPLE
          Dreamcast with a serial line terminal or a remote network
          connection.
 
-config CF_ENABLER
-       bool "Compact Flash Enabler support"
-       depends on SOLUTION_ENGINE || SH_SH03
-       ---help---
-         Compact Flash is a small, removable mass storage device introduced
-         in 1994 originally as a PCMCIA device.  If you say `Y' here, you
-         compile in support for Compact Flash devices directly connected to
-         a SuperH processor.  A Compact Flash FAQ is available at
-         <http://www.compactflash.org/faqs/faq.htm>.
-
-         If your board has "Directly Connected" CompactFlash at area 5 or 6,
-         you may want to enable this option.  Then, you can use CF as
-         primary IDE drive (only tested for SanDisk).
-
-         If in doubt, select 'N'.
-
-choice
-       prompt "Compact Flash Connection Area"
-       depends on CF_ENABLER
-       default CF_AREA6
-
-config CF_AREA5
-       bool "Area5"
-       help
-         If your board has "Directly Connected" CompactFlash, You should
-         select the area where your CF is connected to.
-
-         - "Area5" if CompactFlash is connected to Area 5 (0x14000000)
-         - "Area6" if it is connected to Area 6 (0x18000000)
-
-         "Area6" will work for most boards.
-
-config CF_AREA6
-       bool "Area6"
-
-endchoice
-
-config CF_BASE_ADDR
-       hex
-       depends on CF_ENABLER
-       default "0xb8000000" if CF_AREA6
-       default "0xb4000000" if CF_AREA5
-
 source "arch/sh/drivers/pci/Kconfig"
 
 source "drivers/pci/Kconfig"
@@ -748,13 +719,11 @@ source "fs/Kconfig.binfmt"
 endmenu
 
 menu "Power management options (EXPERIMENTAL)"
-depends on EXPERIMENTAL && SYS_SUPPORTS_PM
+depends on EXPERIMENTAL
 
-config ARCH_SUSPEND_POSSIBLE
-       def_bool y
-       depends on !SMP
+source "kernel/power/Kconfig"
 
-source kernel/power/Kconfig
+source "drivers/cpuidle/Kconfig"
 
 endmenu
 
index e6d2c8b..0d62681 100644 (file)
@@ -98,18 +98,29 @@ config IRQSTACKS
          for handling hard and soft interrupts.  This can help avoid
          overflowing the process kernel stacks.
 
-config SH_KGDB
-       bool "Include KGDB kernel debugger"
-       select FRAME_POINTER
-       select DEBUG_INFO
-       depends on CPU_SH3 || CPU_SH4
+config DUMP_CODE
+       bool "Show disassembly of nearby code in register dumps"
+       depends on DEBUG_KERNEL && SUPERH32
+       default y if DEBUG_BUGVERBOSE
+       default n
+       help
+         This prints out a code trace of the instructions leading up to
+         the faulting instruction as a debugging aid. As this does grow
+         the kernel in size a bit, most users will want to say N here.
+
+         Those looking for more verbose debugging output should say Y.
+
+config SH_NO_BSS_INIT
+       bool "Avoid zeroing BSS (to speed-up startup on suitable platforms)"
+       depends on DEBUG_KERNEL
+       default n
        help
-         Include in-kernel hooks for kgdb, the Linux kernel source level
-         debugger.  See <http://kgdb.sourceforge.net/> for more information.
-         Unless you are intending to debug the kernel, say N here.
+         If running in painfully slow environments, such as an RTL
+         simulation or from remote memory via SHdebug, where the memory
+         can already be gauranteed to ber zeroed on boot, say Y.
 
-menu "KGDB configuration options"
-       depends on SH_KGDB
+         For all other cases, say N. If this option seems perplexing, or
+         you aren't sure, say N.
 
 config MORE_COMPILE_OPTIONS
        bool "Add any additional compile options"
@@ -122,85 +133,16 @@ config COMPILE_OPTIONS
        string "Additional compile arguments"
        depends on MORE_COMPILE_OPTIONS
 
-config KGDB_NMI
-       def_bool n
-       prompt "Enter KGDB on NMI"
-
-config SH_KGDB_CONSOLE
-       def_bool n
-       prompt "Console messages through GDB"
-       depends on !SERIAL_SH_SCI_CONSOLE && SERIAL_SH_SCI=y
-       select SERIAL_CORE_CONSOLE
-
-config KGDB_SYSRQ
-       def_bool y
-       prompt "Allow SysRq 'G' to enter KGDB"
-       depends on MAGIC_SYSRQ
-
-comment "Serial port setup"
-
-config KGDB_DEFPORT
-       int "Port number (ttySCn)"
-       default "1"
-
-config KGDB_DEFBAUD
-       int "Baud rate"
-       default "115200"
-
-choice
-       prompt "Parity"
-       depends on SH_KGDB
-       default KGDB_DEFPARITY_N
-
-config KGDB_DEFPARITY_N
-       bool "None"
-
-config KGDB_DEFPARITY_E
-       bool "Even"
-
-config KGDB_DEFPARITY_O
-       bool "Odd"
-
-endchoice
-
-choice
-       prompt "Data bits"
-       depends on SH_KGDB
-       default KGDB_DEFBITS_8
-
-config KGDB_DEFBITS_8
-       bool "8"
-
-config KGDB_DEFBITS_7
-       bool "7"
-
-endchoice
-
-endmenu
-
-if SUPERH64
-
-config SH64_PROC_ASIDS
-       bool "Debug: report ASIDs through /proc/asids"
-       depends on PROC_FS && MMU
-
 config SH64_SR_WATCH
        bool "Debug: set SR.WATCH to enable hardware watchpoints and trace"
+       depends on SUPERH64
 
 config POOR_MANS_STRACE
        bool "Debug: enable rudimentary strace facility"
+       depends on SUPERH64
        help
          This option allows system calls to be traced to the console.  It also
          aids in detecting kernel stack underflow.  It is useful for debugging
          early-userland problems (e.g. init incurring fatal exceptions.)
 
-config SH_ALPHANUMERIC
-       bool "Enable debug outputs to on-board alphanumeric display"
-       depends on SH_CAYMAN
-
-config SH_NO_BSS_INIT
-       bool "Avoid zeroing BSS (to speed-up startup on suitable platforms)"
-
-endif
-
 endmenu
index c43eb0d..4067b0d 100644 (file)
@@ -32,6 +32,7 @@ cflags-$(CONFIG_CPU_SH4)              := $(call cc-option,-m4,) \
        $(call cc-option,-mno-implicit-fp,-m4-nofpu)
 cflags-$(CONFIG_CPU_SH4A)              += $(call cc-option,-m4a,) \
                                           $(call cc-option,-m4a-nofpu,)
+cflags-$(CONFIG_CPU_SH4AL_DSP)         += $(call cc-option,-m4al,)
 cflags-$(CONFIG_CPU_SH5)               := $(call cc-option,-m5-32media-nofpu,)
 
 ifeq ($(cflags-y),)
@@ -39,22 +40,16 @@ ifeq ($(cflags-y),)
 # In the case where we are stuck with a compiler that has been uselessly
 # restricted to a particular ISA, a favourite default of newer GCCs when
 # extensive multilib targets are not provided, ensure we get the best fit
-# regarding FP generation. This is necessary to avoid references to FP
-# variants in libgcc where integer variants exist, which otherwise result
-# in link errors. This is intentionally stupid (albeit many orders of
-# magnitude less than GCC's default behaviour), as anything with a large
-# number of multilib targets better have been built correctly for
-# the target in mind.
+# regarding FP generation. This is intentionally stupid (albeit many
+# orders of magnitude less than GCC's default behaviour), as anything
+# with a large number of multilib targets better have been built
+# correctly for the target in mind.
 #
 cflags-y       += $(shell $(CC) $(KBUILD_CFLAGS) -print-multi-lib | \
                     grep nofpu | sed q | sed -e 's/^/-/;s/;.*$$//')
-endif
-
-cflags-$(CONFIG_CPU_BIG_ENDIAN)                += -mb
-cflags-$(CONFIG_CPU_LITTLE_ENDIAN)     += -ml
-
-cflags-y       += $(call cc-option,-mno-fdpic)
-
+# At this point, anything goes.
+isaflags-y     := $(call as-option,-Wa$(comma)-isa=any,)
+else
 #
 # -Wa,-isa= tuning implies -Wa,-dsp for the versions of binutils that
 # support it, while -Wa,-dsp by itself limits the range of usable opcodes
@@ -67,7 +62,12 @@ isaflags-y   := $(call as-option,-Wa$(comma)-isa=$(isa-y),)
 
 isaflags-$(CONFIG_SH_DSP)              := \
        $(call as-option,-Wa$(comma)-isa=$(isa-y),-Wa$(comma)-dsp)
+endif
 
+cflags-$(CONFIG_CPU_BIG_ENDIAN)                += -mb
+cflags-$(CONFIG_CPU_LITTLE_ENDIAN)     += -ml
+
+cflags-y       += $(call cc-option,-mno-fdpic)
 cflags-y       += $(isaflags-y) -ffreestanding
 
 cflags-$(CONFIG_MORE_COMPILE_OPTIONS)  += \
@@ -79,6 +79,9 @@ OBJCOPYFLAGS  := -O binary -R .note -R .note.gnu.build-id -R .comment \
 # Give the various platforms the opportunity to set default image types
 defaultimage-$(CONFIG_SUPERH32)                        := zImage
 defaultimage-$(CONFIG_SH_SH7785LCR)            := uImage
+defaultimage-$(CONFIG_SH_RSK)                  := uImage
+defaultimage-$(CONFIG_SH_7206_SOLUTION_ENGINE) := vmlinux
+defaultimage-$(CONFIG_SH_7619_SOLUTION_ENGINE) := vmlinux
 
 # Set some sensible Kbuild defaults
 KBUILD_DEFCONFIG       := shx3_defconfig
@@ -132,6 +135,7 @@ machdir-$(CONFIG_SH_LANDISK)                        += mach-landisk
 machdir-$(CONFIG_SH_TITAN)                     += mach-titan
 machdir-$(CONFIG_SH_LBOX_RE2)                  += mach-lboxre2
 machdir-$(CONFIG_SH_CAYMAN)                    += mach-cayman
+machdir-$(CONFIG_SH_RSK)                       += mach-rsk
 
 ifneq ($(machdir-y),)
 core-y += $(addprefix arch/sh/boards/, \
@@ -173,11 +177,8 @@ KBUILD_CFLAGS              += -pipe $(cflags-y)
 KBUILD_CPPFLAGS                += $(cflags-y)
 KBUILD_AFLAGS          += $(cflags-y)
 
-LIBGCC := $(shell $(CC) $(KBUILD_CFLAGS) -print-libgcc-file-name)
-
 libs-$(CONFIG_SUPERH32)                := arch/sh/lib/ $(libs-y)
 libs-$(CONFIG_SUPERH64)                := arch/sh/lib64/ $(libs-y)
-libs-y                         += $(LIBGCC)
 
 PHONY += maketools FORCE
 
index 50467f9..8619147 100644 (file)
@@ -126,10 +126,12 @@ config SH_RTS7751R2D
          Select RTS7751R2D if configuring for a Renesas Technology
          Sales SH-Graphics board.
 
-config SH_RSK7203
-       bool "RSK7203"
-       select GENERIC_GPIO
-       depends on CPU_SUBTYPE_SH7203
+config SH_RSK
+       bool "Renesas Starter Kit"
+       depends on CPU_SUBTYPE_SH7201 || CPU_SUBTYPE_SH7203
+       help
+        Select this option if configuring for any of the RSK+ MCU
+        evaluation platforms.
 
 config SH_SDK7780
        bool "SDK7780R3"
@@ -253,6 +255,7 @@ source "arch/sh/boards/mach-r2d/Kconfig"
 source "arch/sh/boards/mach-highlander/Kconfig"
 source "arch/sh/boards/mach-sdk7780/Kconfig"
 source "arch/sh/boards/mach-migor/Kconfig"
+source "arch/sh/boards/mach-rsk/Kconfig"
 
 if SH_MAGIC_PANEL_R2
 
index d9efa39..269ae2b 100644 (file)
@@ -3,7 +3,6 @@
 #
 obj-$(CONFIG_SH_AP325RXA)      += board-ap325rxa.o
 obj-$(CONFIG_SH_MAGIC_PANEL_R2)        += board-magicpanelr2.o
-obj-$(CONFIG_SH_RSK7203)       += board-rsk7203.o
 obj-$(CONFIG_SH_SH7785LCR)     += board-sh7785lcr.o
 obj-$(CONFIG_SH_SHMIN)         += board-shmin.o
 obj-$(CONFIG_SH_EDOSK7760)     += board-edosk7760.o
index 8881a64..1c67cba 100644 (file)
@@ -197,6 +197,10 @@ static struct resource lcdc_resources[] = {
                .end    = 0xfe941fff,
                .flags  = IORESOURCE_MEM,
        },
+       [1] = {
+               .start  = 28,
+               .flags  = IORESOURCE_IRQ,
+       },
 };
 
 static struct platform_device lcdc_device = {
@@ -303,6 +307,7 @@ static struct resource ceu_resources[] = {
 
 static struct platform_device ceu_device = {
        .name           = "sh_mobile_ceu",
+       .id             = 0, /* "ceu0" clock */
        .num_resources  = ARRAY_SIZE(ceu_resources),
        .resource       = ceu_resources,
        .dev            = {
@@ -344,7 +349,6 @@ static int __init ap325rxa_devices_setup(void)
        gpio_export(GPIO_PTF7, 0);
 
        /* LCDC */
-       clk_always_enable("mstp200");
        gpio_request(GPIO_FN_LCDD15, NULL);
        gpio_request(GPIO_FN_LCDD14, NULL);
        gpio_request(GPIO_FN_LCDD13, NULL);
@@ -375,7 +379,6 @@ static int __init ap325rxa_devices_setup(void)
        gpio_direction_output(GPIO_PTS3, 1);
 
        /* CEU */
-       clk_always_enable("mstp203");
        gpio_request(GPIO_FN_VIO_CLK2, NULL);
        gpio_request(GPIO_FN_VIO_VD2, NULL);
        gpio_request(GPIO_FN_VIO_HD2, NULL);
index 5cc0867..b1dcbbc 100644 (file)
@@ -22,21 +22,13 @@ static void __init init_shmin_irq(void)
        plat_irq_setup_pins(IRQ_MODE_IRQ);
 }
 
-static void __iomem *shmin_ioport_map(unsigned long port, unsigned int size)
+static void __init shmin_setup(char **cmdline_p)
 {
-       static int dummy;
-
-       if ((port & ~0x1f) == SHMIN_NE_BASE)
-               return (void __iomem *)(SHMIN_IO_BASE + port);
-
-       dummy = 0;
-
-       return &dummy;
-
+       __set_io_port_base(SHMIN_IO_BASE);
 }
 
 static struct sh_machine_vector mv_shmin __initmv = {
        .mv_name        = "SHMIN",
+       .mv_setup       = shmin_setup,
        .mv_init_irq    = init_shmin_irq,
-       .mv_ioport_map  = shmin_ioport_map,
 };
index 489a8f8..cafe1ac 100644 (file)
@@ -2,4 +2,3 @@
 # Makefile for the Hitachi Cayman specific parts of the kernel
 #
 obj-y := setup.o irq.o
-obj-$(CONFIG_HEARTBEAT)        += led.o
index ceb37ae..da62ad5 100644 (file)
@@ -94,31 +94,11 @@ static void ack_cayman_irq(unsigned int irq)
        disable_cayman_irq(irq);
 }
 
-static void end_cayman_irq(unsigned int irq)
-{
-       if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
-               enable_cayman_irq(irq);
-}
-
-static unsigned int startup_cayman_irq(unsigned int irq)
-{
-       enable_cayman_irq(irq);
-       return 0; /* never anything pending */
-}
-
-static void shutdown_cayman_irq(unsigned int irq)
-{
-       disable_cayman_irq(irq);
-}
-
-struct hw_interrupt_type cayman_irq_type = {
-       .typename       = "Cayman-IRQ",
-       .startup        = startup_cayman_irq,
-       .shutdown       = shutdown_cayman_irq,
-       .enable         = enable_cayman_irq,
-       .disable        = disable_cayman_irq,
-       .ack            = ack_cayman_irq,
-       .end            = end_cayman_irq,
+struct irq_chip cayman_irq_type = {
+       .name           = "Cayman-IRQ",
+       .unmask         = enable_cayman_irq,
+       .mask           = disable_cayman_irq,
+       .mask_ack       = ack_cayman_irq,
 };
 
 int cayman_irq_demux(int evt)
@@ -187,8 +167,9 @@ void init_cayman_irq(void)
                return;
        }
 
-       for (i=0; i<NR_EXT_IRQS; i++) {
-               irq_desc[START_EXT_IRQS + i].chip = &cayman_irq_type;
+       for (i = 0; i < NR_EXT_IRQS; i++) {
+               set_irq_chip_and_handler(START_EXT_IRQS + i, &cayman_irq_type,
+                                        handle_level_irq);
        }
 
        /* Setup the SMSC interrupt */
diff --git a/arch/sh/boards/mach-cayman/led.c b/arch/sh/boards/mach-cayman/led.c
deleted file mode 100644 (file)
index a808eac..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * arch/sh/boards/cayman/led.c
- *
- * Copyright (C) 2002 Stuart Menefy <stuart.menefy@st.com>
- *
- * May be copied or modified under the terms of the GNU General Public
- * License.  See linux/COPYING for more information.
- *
- * Flash the LEDs
- */
-#include <asm/io.h>
-
-/*
-** It is supposed these functions to be used for a low level
-** debugging (via Cayman LEDs), hence to be available as soon
-** as possible.
-** Unfortunately Cayman LEDs relies on Cayman EPLD to be mapped
-** (this happen when IRQ are initialized... quite late).
-** These triky dependencies should be removed. Temporary, it
-** may be enough to NOP until EPLD is mapped.
-*/
-
-extern unsigned long epld_virt;
-
-#define LED_ADDR      (epld_virt + 0x008)
-#define HDSP2534_ADDR (epld_virt + 0x100)
-
-void mach_led(int position, int value)
-{
-       if (!epld_virt)
-               return;
-
-       if (value)
-               ctrl_outl(0, LED_ADDR);
-       else
-               ctrl_outl(1, LED_ADDR);
-
-}
-
-void mach_alphanum(int position, unsigned char value)
-{
-       if (!epld_virt)
-               return;
-
-       ctrl_outb(value, HDSP2534_ADDR + 0xe0 + (position << 2));
-}
-
-void mach_alphanum_brightness(int setting)
-{
-       ctrl_outb(setting & 7, HDSP2534_ADDR + 0xc0);
-}
index 67bdc33..f55fc8e 100644 (file)
  */
 
 #include <linux/irq.h>
-#include <asm/io.h>
+#include <linux/io.h>
 #include <asm/irq.h>
 #include <mach/sysasic.h>
 
-/* Dreamcast System ASIC Hardware Events -
-
-   The Dreamcast's System ASIC (a.k.a. Holly) is responsible for receiving
-   hardware events from system peripherals and triggering an SH7750 IRQ.
-   Hardware events can trigger IRQs 13, 11, or 9 depending on which bits are
-   set in the Event Mask Registers (EMRs).  When a hardware event is
-   triggered, it's corresponding bit in the Event Status Registers (ESRs)
-   is set, and that bit should be rewritten to the ESR to acknowledge that
-   event.
-
-   There are three 32-bit ESRs located at 0xa05f8900 - 0xa05f6908.  Event
-   types can be found in include/asm-sh/dreamcast/sysasic.h. There are three
-   groups of EMRs that parallel the ESRs.  Each EMR group corresponds to an
-   IRQ, so 0xa05f6910 - 0xa05f6918 triggers IRQ 13, 0xa05f6920 - 0xa05f6928
-   triggers IRQ 11, and 0xa05f6930 - 0xa05f6938 triggers IRQ 9.
-
-   In the kernel, these events are mapped to virtual IRQs so that drivers can
-   respond to them as they would a normal interrupt.  In order to keep this
-   mapping simple, the events are mapped as:
-
-   6900/6910 - Events  0-31, IRQ 13
-   6904/6924 - Events 32-63, IRQ 11
-   6908/6938 - Events 64-95, IRQ  9
-
-*/
+/*
+ * Dreamcast System ASIC Hardware Events -
+ *
+ * The Dreamcast's System ASIC (a.k.a. Holly) is responsible for receiving
+ * hardware events from system peripherals and triggering an SH7750 IRQ.
+ * Hardware events can trigger IRQs 13, 11, or 9 depending on which bits are
+ * set in the Event Mask Registers (EMRs).  When a hardware event is
+ * triggered, its corresponding bit in the Event Status Registers (ESRs)
+ * is set, and that bit should be rewritten to the ESR to acknowledge that
+ * event.
+ *
+ * There are three 32-bit ESRs located at 0xa05f6900 - 0xa05f6908.  Event
+ * types can be found in arch/sh/include/mach-dreamcast/mach/sysasic.h.
+ * There are three groups of EMRs that parallel the ESRs.  Each EMR group
+ * corresponds to an IRQ, so 0xa05f6910 - 0xa05f6918 triggers IRQ 13,
+ * 0xa05f6920 - 0xa05f6928 triggers IRQ 11, and 0xa05f6930 - 0xa05f6938
+ * triggers IRQ 9.
+ *
+ * In the kernel, these events are mapped to virtual IRQs so that drivers can
+ * respond to them as they would a normal interrupt.  In order to keep this
+ * mapping simple, the events are mapped as:
+ *
+ * 6900/6910 - Events  0-31, IRQ 13
+ * 6904/6924 - Events 32-63, IRQ 11
+ * 6908/6938 - Events 64-95, IRQ  9
+ *
+ */
 
 #define ESR_BASE 0x005f6900    /* Base event status register */
 #define EMR_BASE 0x005f6910    /* Base event mask register */
 
-/* Helps us determine the EMR group that this event belongs to: 0 = 0x6910,
-   1 = 0x6920, 2 = 0x6930; also determine the event offset */
+/*
+ * Helps us determine the EMR group that this event belongs to: 0 = 0x6910,
+ * 1 = 0x6920, 2 = 0x6930; also determine the event offset.
+ */
 #define LEVEL(event) (((event) - HW_EVENT_IRQ_BASE) / 32)
 
 /* Return the hardware event's bit positon within the EMR/ESR */
 #define EVENT_BIT(event) (((event) - HW_EVENT_IRQ_BASE) & 31)
 
-/* For each of these *_irq routines, the IRQ passed in is the virtual IRQ
-   (logically mapped to the corresponding bit for the hardware event). */
+/*
+ * For each of these *_irq routines, the IRQ passed in is the virtual IRQ
+ * (logically mapped to the corresponding bit for the hardware event).
+ */
 
 /* Disable the hardware event by masking its bit in its EMR */
 static inline void disable_systemasic_irq(unsigned int irq)
 {
-        __u32 emr = EMR_BASE + (LEVEL(irq) << 4) + (LEVEL(irq) << 2);
-        __u32 mask;
+       __u32 emr = EMR_BASE + (LEVEL(irq) << 4) + (LEVEL(irq) << 2);
+       __u32 mask;
 
-        mask = inl(emr);
-        mask &= ~(1 << EVENT_BIT(irq));
-        outl(mask, emr);
+       mask = inl(emr);
+       mask &= ~(1 << EVENT_BIT(irq));
+       outl(mask, emr);
 }
 
 /* Enable the hardware event by setting its bit in its EMR */
 static inline void enable_systemasic_irq(unsigned int irq)
 {
-        __u32 emr = EMR_BASE + (LEVEL(irq) << 4) + (LEVEL(irq) << 2);
-        __u32 mask;
+       __u32 emr = EMR_BASE + (LEVEL(irq) << 4) + (LEVEL(irq) << 2);
+       __u32 mask;
 
-        mask = inl(emr);
-        mask |= (1 << EVENT_BIT(irq));
-        outl(mask, emr);
+       mask = inl(emr);
+       mask |= (1 << EVENT_BIT(irq));
+       outl(mask, emr);
 }
 
 /* Acknowledge a hardware event by writing its bit back to its ESR */
-static void ack_systemasic_irq(unsigned int irq)
-{
-        __u32 esr = ESR_BASE + (LEVEL(irq) << 2);
-        disable_systemasic_irq(irq);
-        outl((1 << EVENT_BIT(irq)), esr);
-}
-
-/* After a IRQ has been ack'd and responded to, it needs to be renabled */
-static void end_systemasic_irq(unsigned int irq)
-{
-        if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
-                enable_systemasic_irq(irq);
-}
-
-static unsigned int startup_systemasic_irq(unsigned int irq)
-{
-        enable_systemasic_irq(irq);
-
-        return 0;
-}
-
-static void shutdown_systemasic_irq(unsigned int irq)
+static void mask_ack_systemasic_irq(unsigned int irq)
 {
-        disable_systemasic_irq(irq);
+       __u32 esr = ESR_BASE + (LEVEL(irq) << 2);
+       disable_systemasic_irq(irq);
+       outl((1 << EVENT_BIT(irq)), esr);
 }
 
-struct hw_interrupt_type systemasic_int = {
-        .typename       = "System ASIC",
-        .startup        = startup_systemasic_irq,
-        .shutdown       = shutdown_systemasic_irq,
-        .enable         = enable_systemasic_irq,
-        .disable        = disable_systemasic_irq,
-        .ack            = ack_systemasic_irq,
-        .end            = end_systemasic_irq,
+struct irq_chip systemasic_int = {
+       .name           = "System ASIC",
+       .mask           = disable_systemasic_irq,
+       .mask_ack       = mask_ack_systemasic_irq,
+       .unmask         = enable_systemasic_irq,
 };
 
 /*
@@ -117,37 +101,37 @@ struct hw_interrupt_type systemasic_int = {
  */
 int systemasic_irq_demux(int irq)
 {
-        __u32 emr, esr, status, level;
-        __u32 j, bit;
-
-        switch (irq) {
-                case 13:
-                        level = 0;
-                        break;
-                case 11:
-                        level = 1;
-                        break;
-                case  9:
-                        level = 2;
-                        break;
-                default:
-                        return irq;
-        }
-        emr = EMR_BASE + (level << 4) + (level << 2);
-        esr = ESR_BASE + (level << 2);
-
-        /* Mask the ESR to filter any spurious, unwanted interrupts */
-        status = inl(esr);
-        status &= inl(emr);
-
-        /* Now scan and find the first set bit as the event to map */
-        for (bit = 1, j = 0; j < 32; bit <<= 1, j++) {
-                if (status & bit) {
-                        irq = HW_EVENT_IRQ_BASE + j + (level << 5);
-                        return irq;
-                }
-        }
-
-        /* Not reached */
-        return irq;
+       __u32 emr, esr, status, level;
+       __u32 j, bit;
+
+       switch (irq) {
+       case 13:
+               level = 0;
+               break;
+       case 11:
+               level = 1;
+               break;
+       case  9:
+               level = 2;
+               break;
+       default:
+               return irq;
+       }
+       emr = EMR_BASE + (level << 4) + (level << 2);
+       esr = ESR_BASE + (level << 2);
+
+       /* Mask the ESR to filter any spurious, unwanted interrupts */
+       status = inl(esr);
+       status &= inl(emr);
+
+       /* Now scan and find the first set bit as the event to map */
+       for (bit = 1, j = 0; j < 32; bit <<= 1, j++) {
+               if (status & bit) {
+                       irq = HW_EVENT_IRQ_BASE + j + (level << 5);
+                       return irq;
+               }
+       }
+
+       /* Not reached */
+       return irq;
 }
index 7d944fc..d1bee48 100644 (file)
@@ -28,7 +28,7 @@
 #include <asm/machvec.h>
 #include <mach/sysasic.h>
 
-extern struct hw_interrupt_type systemasic_int;
+extern struct irq_chip systemasic_int;
 extern void aica_time_init(void);
 extern int gapspci_init(void);
 extern int systemasic_irq_demux(int);
@@ -47,7 +47,8 @@ static void __init dreamcast_setup(char **cmdline_p)
 
        /* Assign all virtual IRQs to the System ASIC int. handler */
        for (i = HW_EVENT_IRQ_BASE; i < HW_EVENT_IRQ_MAX; i++)
-               irq_desc[i].chip = &systemasic_int;
+               set_irq_chip_and_handler(i, &systemasic_int,
+                                        handle_level_irq);
 
        board_time_init = aica_time_init;
 
index 7d153e5..5b9c57c 100644 (file)
 
 #include <linux/kernel.h>
 #include <linux/types.h>
-#include <asm/io.h>
+#include <linux/io.h>
 #include <mach/edosk7705.h>
 #include <asm/addrspace.h>
 
 #define SMC_IOADDR     0xA2000000
 
-#define maybebadio(name,port) \
-  printk("bad PC-like io %s for port 0x%lx at 0x%08x\n", \
-        #name, (port), (__u32) __builtin_return_address(0))
-
 /* Map the Ethernet addresses as if it is at 0x300 - 0x320 */
-unsigned long sh_edosk7705_isa_port2addr(unsigned long port)
+static unsigned long sh_edosk7705_isa_port2addr(unsigned long port)
 {
-     if (port >= 0x300 && port < 0x320) {
-         /* SMC91C96 registers are 4 byte aligned rather than the
-          * usual 2 byte!
-          */
-         return SMC_IOADDR + ( (port - 0x300) * 2);
-     }
+       /*
+        * SMC91C96 registers are 4 byte aligned rather than the
+        * usual 2 byte!
+        */
+       if (port >= 0x300 && port < 0x320)
+               return SMC_IOADDR + ((port - 0x300) * 2);
 
-     maybebadio(sh_edosk7705_isa_port2addr, port);
-     return port;
+       maybebadio(port);
+       return port;
 }
 
 /* Trying to read / write bytes on odd-byte boundaries to the Ethernet
@@ -42,53 +38,34 @@ unsigned long sh_edosk7705_isa_port2addr(unsigned long port)
  */
 unsigned char sh_edosk7705_inb(unsigned long port)
 {
-       if (port >= 0x300 && port < 0x320 && port & 0x01) {
-               return (volatile unsigned char)(generic_inw(port -1) >> 8);
-       }
-       return *(volatile unsigned char *)sh_edosk7705_isa_port2addr(port);
-}
+       if (port >= 0x300 && port < 0x320 && port & 0x01)
+               return __raw_readw(port - 1) >> 8;
 
-unsigned int sh_edosk7705_inl(unsigned long port)
-{
-       return *(volatile unsigned long *)port;
+       return __raw_readb(sh_edosk7705_isa_port2addr(port));
 }
 
 void sh_edosk7705_outb(unsigned char value, unsigned long port)
 {
        if (port >= 0x300 && port < 0x320 && port & 0x01) {
-               generic_outw(((unsigned short)value << 8), port -1);
+               __raw_writew(((unsigned short)value << 8), port - 1);
                return;
        }
-       *(volatile unsigned char *)sh_edosk7705_isa_port2addr(port) = value;
-}
 
-void sh_edosk7705_outl(unsigned int value, unsigned long port)
-{
-       *(volatile unsigned long *)port = value;
+       __raw_writeb(value, sh_edosk7705_isa_port2addr(port));
 }
 
 void sh_edosk7705_insb(unsigned long port, void *addr, unsigned long count)
 {
        unsigned char *p = addr;
-       while (count--) *p++ = sh_edosk7705_inb(port);
-}
 
-void sh_edosk7705_insl(unsigned long port, void *addr, unsigned long count)
-{
-       unsigned long *p = (unsigned long*)addr;
        while (count--)
-               *p++ = *(volatile unsigned long *)port;
+               *p++ = sh_edosk7705_inb(port);
 }
 
 void sh_edosk7705_outsb(unsigned long port, const void *addr, unsigned long count)
 {
-       unsigned char *p = (unsigned char*)addr;
-       while (count--) sh_edosk7705_outb(*p++, port);
-}
+       unsigned char *p = (unsigned char *)addr;
 
-void sh_edosk7705_outsl(unsigned long port, const void *addr, unsigned long count)
-{
-       unsigned long *p = (unsigned long*)addr;
-       while (count--) sh_edosk7705_outl(*p++, port);
+       while (count--)
+               sh_edosk7705_outb(*p++, port);
 }
-
index ab3f47b..d59225e 100644 (file)
@@ -9,6 +9,7 @@
  * board by S. Dunn, 2003.
  */
 #include <linux/init.h>
+#include <linux/irq.h>
 #include <asm/machvec.h>
 #include <mach/edosk7705.h>
 
@@ -26,18 +27,10 @@ static struct sh_machine_vector mv_edosk7705 __initmv = {
        .mv_nr_irqs             = 80,
 
        .mv_inb                 = sh_edosk7705_inb,
-       .mv_inl                 = sh_edosk7705_inl,
        .mv_outb                = sh_edosk7705_outb,
-       .mv_outl                = sh_edosk7705_outl,
-
-       .mv_inl_p               = sh_edosk7705_inl,
-       .mv_outl_p              = sh_edosk7705_outl,
 
        .mv_insb                = sh_edosk7705_insb,
-       .mv_insl                = sh_edosk7705_insl,
        .mv_outsb               = sh_edosk7705_outsb,
-       .mv_outsl               = sh_edosk7705_outsl,
 
-       .mv_isa_port2addr       = sh_edosk7705_isa_port2addr,
        .mv_init_irq            = sh_edosk7705_init_irq,
 };
index 64af1f2..d936c1a 100644 (file)
 #include <linux/suspend.h>
 #include <linux/errno.h>
 #include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/gfp.h>
 #include <asm/io.h>
 #include <asm/hd64461.h>
 #include <mach/hp6xx.h>
 #include <cpu/dac.h>
-#include <asm/pm.h>
+#include <asm/freq.h>
+#include <asm/watchdog.h>
+
+#define INTR_OFFSET    0x600
 
 #define STBCR          0xffffff82
 #define STBCR2         0xffffff88
 
+#define STBCR_STBY     0x80
+#define STBCR_MSTP2    0x04
+
+#define MCR            0xffffff68
+#define RTCNT          0xffffff70
+
+#define MCR_RMODE      2
+#define MCR_RFSH       4
+
+extern u8 wakeup_start;
+extern u8 wakeup_end;
+
+static void pm_enter(void)
+{
+       u8 stbcr, csr;
+       u16 frqcr, mcr;
+       u32 vbr_new, vbr_old;
+
+       set_bl_bit();
+
+       /* set wdt */
+       csr = sh_wdt_read_csr();
+       csr &= ~WTCSR_TME;
+       csr |= WTCSR_CKS_4096;
+       sh_wdt_write_csr(csr);
+       csr = sh_wdt_read_csr();
+       sh_wdt_write_cnt(0);
+
+       /* disable PLL1 */
+       frqcr = ctrl_inw(FRQCR);
+       frqcr &= ~(FRQCR_PLLEN | FRQCR_PSTBY);
+       ctrl_outw(frqcr, FRQCR);
+
+       /* enable standby */
+       stbcr = ctrl_inb(STBCR);
+       ctrl_outb(stbcr | STBCR_STBY | STBCR_MSTP2, STBCR);
+
+       /* set self-refresh */
+       mcr = ctrl_inw(MCR);
+       ctrl_outw(mcr & ~MCR_RFSH, MCR);
+
+       /* set interrupt handler */
+       asm volatile("stc vbr, %0" : "=r" (vbr_old));
+       vbr_new = get_zeroed_page(GFP_ATOMIC);
+       udelay(50);
+       memcpy((void*)(vbr_new + INTR_OFFSET),
+              &wakeup_start, &wakeup_end - &wakeup_start);
+       asm volatile("ldc %0, vbr" : : "r" (vbr_new));
+
+       ctrl_outw(0, RTCNT);
+       ctrl_outw(mcr | MCR_RFSH | MCR_RMODE, MCR);
+
+       cpu_sleep();
+
+       asm volatile("ldc %0, vbr" : : "r" (vbr_old));
+
+       free_page(vbr_new);
+
+       /* enable PLL1 */
+       frqcr = ctrl_inw(FRQCR);
+       frqcr |= FRQCR_PSTBY;
+       ctrl_outw(frqcr, FRQCR);
+       udelay(50);
+       frqcr |= FRQCR_PLLEN;
+       ctrl_outw(frqcr, FRQCR);
+
+       ctrl_outb(stbcr, STBCR);
+
+       clear_bl_bit();
+}
+
 static int hp6x0_pm_enter(suspend_state_t state)
 {
        u8 stbcr, stbcr2;
index 1387dd6..4e3588e 100644 (file)
@@ -2,7 +2,4 @@
 # Makefile for the SuperH MicroDev specific parts of the kernel
 #
 
-obj-y   := setup.o irq.o io.o
-
-obj-$(CONFIG_HEARTBEAT)        += led.o
-
+obj-y   := setup.o irq.o io.o fdc37c93xapm.o
diff --git a/arch/sh/boards/mach-microdev/fdc37c93xapm.c b/arch/sh/boards/mach-microdev/fdc37c93xapm.c
new file mode 100644 (file)
index 0000000..458a7cf
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ *
+ * Setup for the SMSC FDC37C93xAPM
+ *
+ * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
+ * Copyright (C) 2003, 2004 SuperH, Inc.
+ * Copyright (C) 2004, 2005 Paul Mundt
+ *
+ * SuperH SH4-202 MicroDev board support.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ */
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <mach/microdev.h>
+
+#define SMSC_CONFIG_PORT_ADDR   (0x3F0)
+#define SMSC_INDEX_PORT_ADDR    SMSC_CONFIG_PORT_ADDR
+#define SMSC_DATA_PORT_ADDR     (SMSC_INDEX_PORT_ADDR + 1)
+
+#define SMSC_ENTER_CONFIG_KEY   0x55
+#define SMSC_EXIT_CONFIG_KEY    0xaa
+
+#define SMCS_LOGICAL_DEV_INDEX  0x07   /* Logical Device Number */
+#define SMSC_DEVICE_ID_INDEX    0x20   /* Device ID */
+#define SMSC_DEVICE_REV_INDEX   0x21   /* Device Revision */
+#define SMSC_ACTIVATE_INDEX     0x30   /* Activate */
+#define SMSC_PRIMARY_BASE_INDEX         0x60   /* Primary Base Address */
+#define SMSC_SECONDARY_BASE_INDEX 0x62 /* Secondary Base Address */
+#define SMSC_PRIMARY_INT_INDEX  0x70   /* Primary Interrupt Select */
+#define SMSC_SECONDARY_INT_INDEX 0x72  /* Secondary Interrupt Select */
+#define SMSC_HDCS0_INDEX        0xf0   /* HDCS0 Address Decoder */
+#define SMSC_HDCS1_INDEX        0xf1   /* HDCS1 Address Decoder */
+
+#define SMSC_IDE1_DEVICE       1       /* IDE #1 logical device */
+#define SMSC_IDE2_DEVICE       2       /* IDE #2 logical device */
+#define SMSC_PARALLEL_DEVICE   3       /* Parallel Port logical device */
+#define SMSC_SERIAL1_DEVICE    4       /* Serial #1 logical device */
+#define SMSC_SERIAL2_DEVICE    5       /* Serial #2 logical device */
+#define SMSC_KEYBOARD_DEVICE   7       /* Keyboard logical device */
+#define SMSC_CONFIG_REGISTERS  8       /* Configuration Registers (Aux I/O) */
+
+#define SMSC_READ_INDEXED(index) ({ \
+       outb((index), SMSC_INDEX_PORT_ADDR); \
+       inb(SMSC_DATA_PORT_ADDR); })
+#define SMSC_WRITE_INDEXED(val, index) ({ \
+       outb((index), SMSC_INDEX_PORT_ADDR); \
+       outb((val),   SMSC_DATA_PORT_ADDR); })
+
+#define        IDE1_PRIMARY_BASE       0x01f0  /* Task File Registe base for IDE #1 */
+#define        IDE1_SECONDARY_BASE     0x03f6  /* Miscellaneous AT registers for IDE #1 */
+#define        IDE2_PRIMARY_BASE       0x0170  /* Task File Registe base for IDE #2 */
+#define        IDE2_SECONDARY_BASE     0x0376  /* Miscellaneous AT registers for IDE #2 */
+
+#define SERIAL1_PRIMARY_BASE   0x03f8
+#define SERIAL2_PRIMARY_BASE   0x02f8
+
+#define        MSB(x)          ( (x) >> 8 )
+#define        LSB(x)          ( (x) & 0xff )
+
+       /* General-Purpose base address on CPU-board FPGA */
+#define        MICRODEV_FPGA_GP_BASE           0xa6100000ul
+
+static int __init smsc_superio_setup(void)
+{
+
+       unsigned char devid, devrev;
+
+               /* Initially the chip is in run state */
+               /* Put it into configuration state */
+       outb(SMSC_ENTER_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR);
+
+               /* Read device ID info */
+       devid  = SMSC_READ_INDEXED(SMSC_DEVICE_ID_INDEX);
+       devrev = SMSC_READ_INDEXED(SMSC_DEVICE_REV_INDEX);
+
+       if ((devid == 0x30) && (devrev == 0x01))
+               printk("SMSC FDC37C93xAPM SuperIO device detected\n");
+       else
+               return -ENODEV;
+
+               /* Select the keyboard device */
+       SMSC_WRITE_INDEXED(SMSC_KEYBOARD_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+               /* enable it */
+       SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+               /* enable the interrupts */
+       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_KEYBOARD, SMSC_PRIMARY_INT_INDEX);
+       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_MOUSE, SMSC_SECONDARY_INT_INDEX);
+
+               /* Select the Serial #1 device */
+       SMSC_WRITE_INDEXED(SMSC_SERIAL1_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+               /* enable it */
+       SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+               /* program with port addresses */
+       SMSC_WRITE_INDEXED(MSB(SERIAL1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
+       SMSC_WRITE_INDEXED(LSB(SERIAL1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
+       SMSC_WRITE_INDEXED(0x00, SMSC_HDCS0_INDEX);
+               /* enable the interrupts */
+       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_SERIAL1, SMSC_PRIMARY_INT_INDEX);
+
+               /* Select the Serial #2 device */
+       SMSC_WRITE_INDEXED(SMSC_SERIAL2_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+               /* enable it */
+       SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+               /* program with port addresses */
+       SMSC_WRITE_INDEXED(MSB(SERIAL2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
+       SMSC_WRITE_INDEXED(LSB(SERIAL2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
+       SMSC_WRITE_INDEXED(0x00, SMSC_HDCS0_INDEX);
+               /* enable the interrupts */
+       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_SERIAL2, SMSC_PRIMARY_INT_INDEX);
+
+               /* Select the IDE#1 device */
+       SMSC_WRITE_INDEXED(SMSC_IDE1_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+               /* enable it */
+       SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+               /* program with port addresses */
+       SMSC_WRITE_INDEXED(MSB(IDE1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
+       SMSC_WRITE_INDEXED(LSB(IDE1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
+       SMSC_WRITE_INDEXED(MSB(IDE1_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+0);
+       SMSC_WRITE_INDEXED(LSB(IDE1_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+1);
+       SMSC_WRITE_INDEXED(0x0c, SMSC_HDCS0_INDEX);
+       SMSC_WRITE_INDEXED(0x00, SMSC_HDCS1_INDEX);
+               /* select the interrupt */
+       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_IDE1, SMSC_PRIMARY_INT_INDEX);
+
+               /* Select the IDE#2 device */
+       SMSC_WRITE_INDEXED(SMSC_IDE2_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+               /* enable it */
+       SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+               /* program with port addresses */
+       SMSC_WRITE_INDEXED(MSB(IDE2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
+       SMSC_WRITE_INDEXED(LSB(IDE2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
+       SMSC_WRITE_INDEXED(MSB(IDE2_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+0);
+       SMSC_WRITE_INDEXED(LSB(IDE2_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+1);
+               /* select the interrupt */
+       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_IDE2, SMSC_PRIMARY_INT_INDEX);
+
+               /* Select the configuration registers */
+       SMSC_WRITE_INDEXED(SMSC_CONFIG_REGISTERS, SMCS_LOGICAL_DEV_INDEX);
+               /* enable the appropriate GPIO pins for IDE functionality:
+                * bit[0]   In/Out              1==input;  0==output
+                * bit[1]   Polarity            1==invert; 0==no invert
+                * bit[2]   Int Enb #1          1==Enable Combined IRQ #1; 0==disable
+                * bit[3:4] Function Select     00==original; 01==Alternate Function #1
+                */
+       SMSC_WRITE_INDEXED(0x00, 0xc2); /* GP42 = nIDE1_OE */
+       SMSC_WRITE_INDEXED(0x01, 0xc5); /* GP45 = IDE1_IRQ */
+       SMSC_WRITE_INDEXED(0x00, 0xc6); /* GP46 = nIOROP */
+       SMSC_WRITE_INDEXED(0x00, 0xc7); /* GP47 = nIOWOP */
+       SMSC_WRITE_INDEXED(0x08, 0xe8); /* GP20 = nIDE2_OE */
+
+               /* Exit the configuration state */
+       outb(SMSC_EXIT_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR);
+
+       return 0;
+}
+device_initcall(smsc_superio_setup);
index 702753c..b551963 100644 (file)
@@ -67,27 +67,13 @@ static const struct {
 
 static void enable_microdev_irq(unsigned int irq);
 static void disable_microdev_irq(unsigned int irq);
-
-       /* shutdown is same as "disable" */
-#define shutdown_microdev_irq disable_microdev_irq
-
 static void mask_and_ack_microdev(unsigned int);
-static void end_microdev_irq(unsigned int irq);
-
-static unsigned int startup_microdev_irq(unsigned int irq)
-{
-       enable_microdev_irq(irq);
-       return 0;               /* never anything pending */
-}
 
-static struct hw_interrupt_type microdev_irq_type = {
-       .typename = "MicroDev-IRQ",
-       .startup = startup_microdev_irq,
-       .shutdown = shutdown_microdev_irq,
-       .enable = enable_microdev_irq,
-       .disable = disable_microdev_irq,
+static struct irq_chip microdev_irq_type = {
+       .name = "MicroDev-IRQ",
+       .unmask = enable_microdev_irq,
+       .mask = disable_microdev_irq,
        .ack = mask_and_ack_microdev,
-       .end = end_microdev_irq
 };
 
 static void disable_microdev_irq(unsigned int irq)
@@ -130,11 +116,11 @@ static void enable_microdev_irq(unsigned int irq)
        ctrl_outl(MICRODEV_FPGA_INTC_MASK(fpgaIrq), MICRODEV_FPGA_INTENB_REG);
 }
 
-       /* This functions sets the desired irq handler to be a MicroDev type */
+/* This function sets the desired irq handler to be a MicroDev type */
 static void __init make_microdev_irq(unsigned int irq)
 {
        disable_irq_nosync(irq);
-       irq_desc[irq].chip = &microdev_irq_type;
+       set_irq_chip_and_handler(irq, &microdev_irq_type, handle_level_irq);
        disable_microdev_irq(irq);
 }
 
@@ -143,17 +129,11 @@ static void mask_and_ack_microdev(unsigned int irq)
        disable_microdev_irq(irq);
 }
 
-static void end_microdev_irq(unsigned int irq)
-{
-       if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
-               enable_microdev_irq(irq);
-}
-
 extern void __init init_microdev_irq(void)
 {
        int i;
 
-               /* disable interrupts on the FPGA INTC register */
+       /* disable interrupts on the FPGA INTC register */
        ctrl_outl(~0ul, MICRODEV_FPGA_INTDSB_REG);
 
        for (i = 0; i < NUM_EXTERNAL_IRQS; i++)
@@ -179,5 +159,3 @@ extern void microdev_print_fpga_intc_status(void)
        printk("FPGA_INTPRI[3..0] = %08x:%08x:%08x:%08x\n", *intprid, *intpric, *intprib, *intpria);
        printk("-------------------------------------------------------------------------------\n");
 }
-
-
diff --git a/arch/sh/boards/mach-microdev/led.c b/arch/sh/boards/mach-microdev/led.c
deleted file mode 100644 (file)
index 36e54b4..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * linux/arch/sh/boards/superh/microdev/led.c
- *
- * Copyright (C) 2002 Stuart Menefy <stuart.menefy@st.com>
- * Copyright (C) 2003 Richard Curnow (Richard.Curnow@superh.com)
- *
- * May be copied or modified under the terms of the GNU General Public
- * License.  See linux/COPYING for more information.
- *
- */
-
-#include <asm/io.h>
-
-#define LED_REGISTER 0xa6104d20
-
-static void mach_led_d9(int value)
-{
-       unsigned long reg;
-       reg = ctrl_inl(LED_REGISTER);
-       reg &= ~1;
-       reg |= (value & 1);
-       ctrl_outl(reg, LED_REGISTER);
-       return;
-}
-
-static void mach_led_d10(int value)
-{
-       unsigned long reg;
-       reg = ctrl_inl(LED_REGISTER);
-       reg &= ~2;
-       reg |= ((value & 1) << 1);
-       ctrl_outl(reg, LED_REGISTER);
-       return;
-}
-
-
-#ifdef CONFIG_HEARTBEAT
-#include <linux/sched.h>
-
-static unsigned char banner_table[] = {
-       0x11, 0x01, 0x11, 0x01, 0x11, 0x03,
-       0x11, 0x01, 0x11, 0x01, 0x13, 0x03,
-       0x11, 0x01, 0x13, 0x01, 0x13, 0x01, 0x11, 0x03,
-       0x11, 0x03,
-       0x11, 0x01, 0x13, 0x01, 0x11, 0x03,
-       0x11, 0x01, 0x11, 0x01, 0x11, 0x01, 0x11, 0x07,
-       0x13, 0x01, 0x13, 0x03,
-       0x11, 0x01, 0x11, 0x03,
-       0x13, 0x01, 0x11, 0x01, 0x13, 0x01, 0x11, 0x03,
-       0x11, 0x01, 0x13, 0x01, 0x11, 0x03,
-       0x13, 0x01, 0x13, 0x01, 0x13, 0x03,
-       0x13, 0x01, 0x11, 0x01, 0x11, 0x03,
-       0x11, 0x03,
-       0x11, 0x01, 0x11, 0x01, 0x11, 0x01, 0x13, 0x07,
-       0xff
-};
-
-static void banner(void)
-{
-       static int pos = 0;
-       static int count = 0;
-
-       if (count) {
-               count--;
-       } else {
-               int val = banner_table[pos];
-               if (val == 0xff) {
-                       pos = 0;
-                       val = banner_table[pos];
-               }
-               pos++;
-               mach_led_d10((val >> 4) & 1);
-               count = 10 * (val & 0xf);
-       }
-}
-
-/* From heartbeat_harp in the stboards directory */
-/* acts like an actual heart beat -- ie thump-thump-pause... */
-void microdev_heartbeat(void)
-{
-       static unsigned cnt = 0, period = 0, dist = 0;
-
-       if (cnt == 0 || cnt == dist)
-               mach_led_d9(1);
-       else if (cnt == 7 || cnt == dist+7)
-               mach_led_d9(0);
-
-       if (++cnt > period) {
-               cnt = 0;
-               /* The hyperbolic function below modifies the heartbeat period
-                * length in dependency of the current (5min) load. It goes
-                * through the points f(0)=126, f(1)=86, f(5)=51,
-                * f(inf)->30. */
-               period = ((672<<FSHIFT)/(5*avenrun[0]+(7<<FSHIFT))) + 30;
-               dist = period / 4;
-       }
-
-       banner();
-}
-
-#endif
index a9202fe..d1df2a4 100644 (file)
 #include <mach/microdev.h>
 #include <asm/io.h>
 #include <asm/machvec.h>
-
-extern void microdev_heartbeat(void);
-
-
-/****************************************************************************/
-
-
-       /*
-        * Setup for the SMSC FDC37C93xAPM
-        */
-#define SMSC_CONFIG_PORT_ADDR   (0x3F0)
-#define SMSC_INDEX_PORT_ADDR    SMSC_CONFIG_PORT_ADDR
-#define SMSC_DATA_PORT_ADDR     (SMSC_INDEX_PORT_ADDR + 1)
-
-#define SMSC_ENTER_CONFIG_KEY   0x55
-#define SMSC_EXIT_CONFIG_KEY    0xaa
-
-#define SMCS_LOGICAL_DEV_INDEX          0x07   /* Logical Device Number */
-#define SMSC_DEVICE_ID_INDEX    0x20   /* Device ID */
-#define SMSC_DEVICE_REV_INDEX   0x21   /* Device Revision */
-#define SMSC_ACTIVATE_INDEX     0x30   /* Activate */
-#define SMSC_PRIMARY_BASE_INDEX         0x60   /* Primary Base Address */
-#define SMSC_SECONDARY_BASE_INDEX 0x62 /* Secondary Base Address */
-#define SMSC_PRIMARY_INT_INDEX  0x70   /* Primary Interrupt Select */
-#define SMSC_SECONDARY_INT_INDEX 0x72  /* Secondary Interrupt Select */
-#define SMSC_HDCS0_INDEX        0xf0   /* HDCS0 Address Decoder */
-#define SMSC_HDCS1_INDEX        0xf1   /* HDCS1 Address Decoder */
-
-#define SMSC_IDE1_DEVICE       1       /* IDE #1 logical device */
-#define SMSC_IDE2_DEVICE       2       /* IDE #2 logical device */
-#define SMSC_PARALLEL_DEVICE   3       /* Parallel Port logical device */
-#define SMSC_SERIAL1_DEVICE    4       /* Serial #1 logical device */
-#define SMSC_SERIAL2_DEVICE    5       /* Serial #2 logical device */
-#define SMSC_KEYBOARD_DEVICE   7       /* Keyboard logical device */
-#define SMSC_CONFIG_REGISTERS  8       /* Configuration Registers (Aux I/O) */
-
-#define SMSC_READ_INDEXED(index) ({ \
-       outb((index), SMSC_INDEX_PORT_ADDR); \
-       inb(SMSC_DATA_PORT_ADDR); })
-#define SMSC_WRITE_INDEXED(val, index) ({ \
-       outb((index), SMSC_INDEX_PORT_ADDR); \
-       outb((val),   SMSC_DATA_PORT_ADDR); })
-
-#define        IDE1_PRIMARY_BASE       0x01f0  /* Task File Registe base for IDE #1 */
-#define        IDE1_SECONDARY_BASE     0x03f6  /* Miscellaneous AT registers for IDE #1 */
-#define        IDE2_PRIMARY_BASE       0x0170  /* Task File Registe base for IDE #2 */
-#define        IDE2_SECONDARY_BASE     0x0376  /* Miscellaneous AT registers for IDE #2 */
-
-#define SERIAL1_PRIMARY_BASE   0x03f8
-#define SERIAL2_PRIMARY_BASE   0x02f8
-
-#define        MSB(x)          ( (x) >> 8 )
-#define        LSB(x)          ( (x) & 0xff )
-
-       /* General-Purpose base address on CPU-board FPGA */
-#define        MICRODEV_FPGA_GP_BASE           0xa6100000ul
-
-       /* assume a Keyboard Controller is present */
-int microdev_kbd_controller_present = 1;
+#include <asm/sizes.h>
 
 static struct resource smc91x_resources[] = {
        [0] = {
                .start          = 0x300,
-               .end            = 0x300 + 0x0001000 - 1,
+               .end            = 0x300 + SZ_4K - 1,
                .flags          = IORESOURCE_MEM,
        },
        [1] = {
@@ -97,7 +39,6 @@ static struct platform_device smc91x_device = {
        .resource       = smc91x_resources,
 };
 
-#ifdef CONFIG_FB_S1D13XXX
 static struct s1d13xxxfb_regval s1d13806_initregs[] = {
        { S1DREG_MISC,                  0x00 },
        { S1DREG_COM_DISP_MODE,         0x00 },
@@ -216,12 +157,12 @@ static struct s1d13xxxfb_pdata s1d13806_platform_data = {
 static struct resource s1d13806_resources[] = {
        [0] = {
                .start          = 0x07200000,
-               .end            = 0x07200000 + 0x00200000 - 1,
+               .end            = 0x07200000 + SZ_2M - 1,
                .flags          = IORESOURCE_MEM,
        },
        [1] = {
                .start          = 0x07000000,
-               .end            = 0x07000000 + 0x00200000 - 1,
+               .end            = 0x07000000 + SZ_2M - 1,
                .flags          = IORESOURCE_MEM,
        },
 };
@@ -236,145 +177,24 @@ static struct platform_device s1d13806_device = {
                .platform_data  = &s1d13806_platform_data,
        },
 };
-#endif
 
 static struct platform_device *microdev_devices[] __initdata = {
        &smc91x_device,
-#ifdef CONFIG_FB_S1D13XXX
        &s1d13806_device,
-#endif
 };
 
 static int __init microdev_devices_setup(void)
 {
        return platform_add_devices(microdev_devices, ARRAY_SIZE(microdev_devices));
 }
-
-/*
- * Setup for the SMSC FDC37C93xAPM
- */
-static int __init smsc_superio_setup(void)
-{
-
-       unsigned char devid, devrev;
-
-               /* Initially the chip is in run state */
-               /* Put it into configuration state */
-       outb(SMSC_ENTER_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR);
-
-               /* Read device ID info */
-       devid  = SMSC_READ_INDEXED(SMSC_DEVICE_ID_INDEX);
-       devrev = SMSC_READ_INDEXED(SMSC_DEVICE_REV_INDEX);
-       if ( (devid==0x30) && (devrev==0x01) )
-       {
-               printk("SMSC FDC37C93xAPM SuperIO device detected\n");
-       }
-       else
-       {               /* not the device identity we expected */
-               printk("Not detected a SMSC FDC37C93xAPM SuperIO device (devid=0x%02x, rev=0x%02x)\n",
-                       devid, devrev);
-                       /* inform the keyboard driver that we have no keyboard controller */
-               microdev_kbd_controller_present = 0;
-                       /* little point in doing anything else in this functon */
-               return 0;
-       }
-
-               /* Select the keyboard device */
-       SMSC_WRITE_INDEXED(SMSC_KEYBOARD_DEVICE, SMCS_LOGICAL_DEV_INDEX);
-               /* enable it */
-       SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
-               /* enable the interrupts */
-       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_KEYBOARD, SMSC_PRIMARY_INT_INDEX);
-       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_MOUSE, SMSC_SECONDARY_INT_INDEX);
-
-               /* Select the Serial #1 device */
-       SMSC_WRITE_INDEXED(SMSC_SERIAL1_DEVICE, SMCS_LOGICAL_DEV_INDEX);
-               /* enable it */
-       SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
-               /* program with port addresses */
-       SMSC_WRITE_INDEXED(MSB(SERIAL1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
-       SMSC_WRITE_INDEXED(LSB(SERIAL1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
-       SMSC_WRITE_INDEXED(0x00, SMSC_HDCS0_INDEX);
-               /* enable the interrupts */
-       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_SERIAL1, SMSC_PRIMARY_INT_INDEX);
-
-               /* Select the Serial #2 device */
-       SMSC_WRITE_INDEXED(SMSC_SERIAL2_DEVICE, SMCS_LOGICAL_DEV_INDEX);
-               /* enable it */
-       SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
-               /* program with port addresses */
-       SMSC_WRITE_INDEXED(MSB(SERIAL2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
-       SMSC_WRITE_INDEXED(LSB(SERIAL2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
-       SMSC_WRITE_INDEXED(0x00, SMSC_HDCS0_INDEX);
-               /* enable the interrupts */
-       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_SERIAL2, SMSC_PRIMARY_INT_INDEX);
-
-               /* Select the IDE#1 device */
-       SMSC_WRITE_INDEXED(SMSC_IDE1_DEVICE, SMCS_LOGICAL_DEV_INDEX);
-               /* enable it */
-       SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
-               /* program with port addresses */
-       SMSC_WRITE_INDEXED(MSB(IDE1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
-       SMSC_WRITE_INDEXED(LSB(IDE1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
-       SMSC_WRITE_INDEXED(MSB(IDE1_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+0);
-       SMSC_WRITE_INDEXED(LSB(IDE1_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+1);
-       SMSC_WRITE_INDEXED(0x0c, SMSC_HDCS0_INDEX);
-       SMSC_WRITE_INDEXED(0x00, SMSC_HDCS1_INDEX);
-               /* select the interrupt */
-       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_IDE1, SMSC_PRIMARY_INT_INDEX);
-
-               /* Select the IDE#2 device */
-       SMSC_WRITE_INDEXED(SMSC_IDE2_DEVICE, SMCS_LOGICAL_DEV_INDEX);
-               /* enable it */
-       SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
-               /* program with port addresses */
-       SMSC_WRITE_INDEXED(MSB(IDE2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
-       SMSC_WRITE_INDEXED(LSB(IDE2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
-       SMSC_WRITE_INDEXED(MSB(IDE2_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+0);
-       SMSC_WRITE_INDEXED(LSB(IDE2_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+1);
-               /* select the interrupt */
-       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_IDE2, SMSC_PRIMARY_INT_INDEX);
-
-               /* Select the configuration registers */
-       SMSC_WRITE_INDEXED(SMSC_CONFIG_REGISTERS, SMCS_LOGICAL_DEV_INDEX);
-               /* enable the appropriate GPIO pins for IDE functionality:
-                * bit[0]   In/Out              1==input;  0==output
-                * bit[1]   Polarity            1==invert; 0==no invert
-                * bit[2]   Int Enb #1          1==Enable Combined IRQ #1; 0==disable
-                * bit[3:4] Function Select     00==original; 01==Alternate Function #1
-                */
-       SMSC_WRITE_INDEXED(0x00, 0xc2); /* GP42 = nIDE1_OE */
-       SMSC_WRITE_INDEXED(0x01, 0xc5); /* GP45 = IDE1_IRQ */
-       SMSC_WRITE_INDEXED(0x00, 0xc6); /* GP46 = nIOROP */
-       SMSC_WRITE_INDEXED(0x00, 0xc7); /* GP47 = nIOWOP */
-       SMSC_WRITE_INDEXED(0x08, 0xe8); /* GP20 = nIDE2_OE */
-
-               /* Exit the configuration state */
-       outb(SMSC_EXIT_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR);
-
-       return 0;
-}
-
-static void __init microdev_setup(char **cmdline_p)
-{
-       int * const fpgaRevisionRegister = (int*)(MICRODEV_FPGA_GP_BASE + 0x8ul);
-       const int fpgaRevision = *fpgaRevisionRegister;
-       int * const CacheControlRegister = (int*)CCR;
-
-       device_initcall(microdev_devices_setup);
-       device_initcall(smsc_superio_setup);
-
-       printk("SuperH %s board (FPGA rev: 0x%0x, CCR: 0x%0x)\n",
-               get_system_type(), fpgaRevision, *CacheControlRegister);
-}
+device_initcall(microdev_devices_setup);
 
 /*
  * The Machine Vector
  */
 static struct sh_machine_vector mv_sh4202_microdev __initmv = {
        .mv_name                = "SH4-202 MicroDev",
-       .mv_setup               = microdev_setup,
-       .mv_nr_irqs             = 72,           /* QQQ need to check this - use the MACRO */
+       .mv_nr_irqs             = 72,
 
        .mv_inb                 = microdev_inb,
        .mv_inw                 = microdev_inw,
@@ -398,8 +218,4 @@ static struct sh_machine_vector mv_sh4202_microdev __initmv = {
        .mv_outsl               = microdev_outsl,
 
        .mv_init_irq            = init_microdev_irq,
-
-#ifdef CONFIG_HEARTBEAT
-       .mv_heartbeat           = microdev_heartbeat,
-#endif
 };
index 9752819..cc14081 100644 (file)
@@ -89,6 +89,7 @@ static struct resource sh_keysc_resources[] = {
 
 static struct platform_device sh_keysc_device = {
        .name           = "sh_keysc",
+       .id             = 0, /* "keysc0" clock */
        .num_resources  = ARRAY_SIZE(sh_keysc_resources),
        .resource       = sh_keysc_resources,
        .dev    = {
@@ -261,6 +262,8 @@ static struct sh_mobile_lcdc_info sh_mobile_lcdc_info = {
                .sys_bus_cfg = {
                        .ldmt2r = 0x06000a09,
                        .ldmt3r = 0x180e3418,
+                       /* set 1s delay to encourage fsync() */
+                       .deferred_io_msec = 1000,
                },
        }
 #endif
@@ -273,6 +276,10 @@ static struct resource migor_lcdc_resources[] = {
                .end    = 0xfe941fff,
                .flags  = IORESOURCE_MEM,
        },
+       [1] = {
+               .start  = 28,
+               .flags  = IORESOURCE_IRQ,
+       },
 };
 
 static struct platform_device migor_lcdc_device = {
@@ -300,6 +307,7 @@ static void camera_power_on(void)
        gpio_set_value(GPIO_PTT3, 0);
        mdelay(10);
        gpio_set_value(GPIO_PTT3, 1);
+       mdelay(10); /* wait to let chip come out of reset */
 }
 
 static void camera_power_off(void)
@@ -432,6 +440,7 @@ static struct resource migor_ceu_resources[] = {
 
 static struct platform_device migor_ceu_device = {
        .name           = "sh_mobile_ceu",
+       .id             = 0, /* "ceu0" clock */
        .num_resources  = ARRAY_SIZE(migor_ceu_resources),
        .resource       = migor_ceu_resources,
        .dev    = {
@@ -479,7 +488,6 @@ static int __init migor_devices_setup(void)
        ctrl_outl(0x00110080, BSC_CS4WCR);
 
        /* KEYSC */
-       clk_always_enable("mstp214"); /* KEYSC */
        gpio_request(GPIO_FN_KEYOUT0, NULL);
        gpio_request(GPIO_FN_KEYOUT1, NULL);
        gpio_request(GPIO_FN_KEYOUT2, NULL);
@@ -501,7 +509,6 @@ static int __init migor_devices_setup(void)
        gpio_request(GPIO_FN_IRQ6, NULL);
 
        /* LCD Panel */
-       clk_always_enable("mstp200"); /* LCDC */
 #ifdef CONFIG_SH_MIGOR_QVGA /* LCDC - QVGA - Enable SYS Interface signals */
        gpio_request(GPIO_FN_LCDD17, NULL);
        gpio_request(GPIO_FN_LCDD16, NULL);
@@ -554,7 +561,6 @@ static int __init migor_devices_setup(void)
 #endif
 
        /* CEU */
-       clk_always_enable("mstp203"); /* CEU */
        gpio_request(GPIO_FN_VIO_CLK2, NULL);
        gpio_request(GPIO_FN_VIO_VD2, NULL);
        gpio_request(GPIO_FN_VIO_HD2, NULL);
@@ -589,12 +595,3 @@ static int __init migor_devices_setup(void)
        return platform_add_devices(migor_devices, ARRAY_SIZE(migor_devices));
 }
 __initcall(migor_devices_setup);
-
-static void __init migor_setup(char **cmdline_p)
-{
-}
-
-static struct sh_machine_vector mv_migor __initmv = {
-       .mv_name                = "Migo-R",
-       .mv_setup               = migor_setup,
-};
diff --git a/arch/sh/boards/mach-rsk/Kconfig b/arch/sh/boards/mach-rsk/Kconfig
new file mode 100644 (file)
index 0000000..bff095d
--- /dev/null
@@ -0,0 +1,18 @@
+if SH_RSK
+
+choice
+       prompt "RSK+ options"
+       default SH_RSK7203
+
+config SH_RSK7201
+       bool "RSK7201"
+       depends on CPU_SUBTYPE_SH7201
+
+config SH_RSK7203
+       bool "RSK7203"
+       select GENERIC_GPIO
+       depends on CPU_SUBTYPE_SH7203
+
+endchoice
+
+endif
diff --git a/arch/sh/boards/mach-rsk/Makefile b/arch/sh/boards/mach-rsk/Makefile
new file mode 100644 (file)
index 0000000..498da75
--- /dev/null
@@ -0,0 +1,2 @@
+obj-y                          := setup.o
+obj-$(CONFIG_SH_RSK7203)       += devices-rsk7203.o
similarity index 58%
rename from arch/sh/boards/board-rsk7203.c
rename to arch/sh/boards/mach-rsk/devices-rsk7203.c
index 58266f0..73f743b 100644 (file)
@@ -50,73 +50,6 @@ static struct platform_device smc911x_device = {
        },
 };
 
-static const char *probes[] = { "cmdlinepart", NULL };
-
-static struct mtd_partition *parsed_partitions;
-
-static struct mtd_partition rsk7203_partitions[] = {
-       {
-               .name           = "Bootloader",
-               .offset         = 0x00000000,
-               .size           = 0x00040000,
-               .mask_flags     = MTD_WRITEABLE,
-       }, {
-               .name           = "Kernel",
-               .offset         = MTDPART_OFS_NXTBLK,
-               .size           = 0x001c0000,
-       }, {
-               .name           = "Flash_FS",
-               .offset         = MTDPART_OFS_NXTBLK,
-               .size           = MTDPART_SIZ_FULL,
-       }
-};
-
-static struct physmap_flash_data flash_data = {
-       .width          = 2,
-};
-
-static struct resource flash_resource = {
-       .start          = 0x20000000,
-       .end            = 0x20400000,
-       .flags          = IORESOURCE_MEM,
-};
-
-static struct platform_device flash_device = {
-       .name           = "physmap-flash",
-       .id             = -1,
-       .resource       = &flash_resource,
-       .num_resources  = 1,
-       .dev            = {
-               .platform_data = &flash_data,
-       },
-};
-
-static struct mtd_info *flash_mtd;
-
-static struct map_info rsk7203_flash_map = {
-       .name           = "RSK+ Flash",
-       .size           = 0x400000,
-       .bankwidth      = 2,
-};
-
-static void __init set_mtd_partitions(void)
-{
-       int nr_parts = 0;
-
-       simple_map_init(&rsk7203_flash_map);
-       flash_mtd = do_map_probe("cfi_probe", &rsk7203_flash_map);
-       nr_parts = parse_mtd_partitions(flash_mtd, probes,
-                                       &parsed_partitions, 0);
-       /* If there is no partition table, used the hard coded table */
-       if (nr_parts <= 0) {
-               flash_data.parts = rsk7203_partitions;
-               flash_data.nr_parts = ARRAY_SIZE(rsk7203_partitions);
-       } else {
-               flash_data.nr_parts = nr_parts;
-               flash_data.parts = parsed_partitions;
-       }
-}
-
 static struct gpio_led rsk7203_gpio_leds[] = {
        {
                .name                   = "green",
@@ -155,7 +88,6 @@ static struct platform_device led_device = {
 
 static struct platform_device *rsk7203_devices[] __initdata = {
        &smc911x_device,
-       &flash_device,
        &led_device,
 };
 
@@ -165,15 +97,7 @@ static int __init rsk7203_devices_setup(void)
        gpio_request(GPIO_FN_TXD0, NULL);
        gpio_request(GPIO_FN_RXD0, NULL);
 
-       set_mtd_partitions();
        return platform_add_devices(rsk7203_devices,
                                    ARRAY_SIZE(rsk7203_devices));
 }
 device_initcall(rsk7203_devices_setup);
-
-/*
- * The Machine Vector
- */
-static struct sh_machine_vector mv_rsk7203 __initmv = {
-       .mv_name        = "RSK+7203",
-};
diff --git a/arch/sh/boards/mach-rsk/setup.c b/arch/sh/boards/mach-rsk/setup.c
new file mode 100644 (file)
index 0000000..af64d03
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Renesas Technology Europe RSK+ Support.
+ *
+ * Copyright (C) 2008 Paul Mundt
+ * Copyright (C) 2008 Peter Griffin <pgriffin@mpc-data.co.uk>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/physmap.h>
+#include <linux/mtd/map.h>
+#include <asm/machvec.h>
+#include <asm/io.h>
+
+static const char *probes[] = { "cmdlinepart", NULL };
+
+static struct mtd_partition *parsed_partitions;
+
+static struct mtd_partition rsk_partitions[] = {
+       {
+               .name           = "Bootloader",
+               .offset         = 0x00000000,
+               .size           = 0x00040000,
+               .mask_flags     = MTD_WRITEABLE,
+       }, {
+               .name           = "Kernel",
+               .offset         = MTDPART_OFS_NXTBLK,
+               .size           = 0x001c0000,
+       }, {
+               .name           = "Flash_FS",
+               .offset         = MTDPART_OFS_NXTBLK,
+               .size           = MTDPART_SIZ_FULL,
+       }
+};
+
+static struct physmap_flash_data flash_data = {
+       .width          = 2,
+};
+
+static struct resource flash_resource = {
+       .start          = 0x20000000,
+       .end            = 0x20400000,
+       .flags          = IORESOURCE_MEM,
+};
+
+static struct platform_device flash_device = {
+       .name           = "physmap-flash",
+       .id             = -1,
+       .resource       = &flash_resource,
+       .num_resources  = 1,
+       .dev            = {
+               .platform_data = &flash_data,
+       },
+};
+
+static struct mtd_info *flash_mtd;
+
+static struct map_info rsk_flash_map = {
+       .name           = "RSK+ Flash",
+       .size           = 0x400000,
+       .bankwidth      = 2,
+};
+
+static void __init set_mtd_partitions(void)
+{
+       int nr_parts = 0;
+
+       simple_map_init(&rsk_flash_map);
+       flash_mtd = do_map_probe("cfi_probe", &rsk_flash_map);
+       nr_parts = parse_mtd_partitions(flash_mtd, probes,
+                                       &parsed_partitions, 0);
+       /* If there is no partition table, used the hard coded table */
+       if (nr_parts <= 0) {
+               flash_data.parts = rsk_partitions;
+               flash_data.nr_parts = ARRAY_SIZE(rsk_partitions);
+       } else {
+               flash_data.nr_parts = nr_parts;
+               flash_data.parts = parsed_partitions;
+       }
+}
+
+static struct platform_device *rsk_devices[] __initdata = {
+       &flash_device,
+};
+
+static int __init rsk_devices_setup(void)
+{
+       set_mtd_partitions();
+       return platform_add_devices(rsk_devices,
+                                   ARRAY_SIZE(rsk_devices));
+}
+device_initcall(rsk_devices_setup);
+
+/*
+ * The Machine Vector
+ */
+static struct sh_machine_vector mv_rsk __initmv = {
+       .mv_name        = "RSK+",
+};
index 3024796..4c3666a 100644 (file)
@@ -2,4 +2,4 @@
 # Makefile for the 7343 SolutionEngine specific parts of the kernel
 #
 
-obj-y   := setup.o io.o irq.o
+obj-y   := setup.o irq.o
diff --git a/arch/sh/boards/mach-se/7343/io.c b/arch/sh/boards/mach-se/7343/io.c
deleted file mode 100644 (file)
index 8741abc..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * arch/sh/boards/se/7343/io.c
- *
- * I/O routine for SH-Mobile3AS 7343 SolutionEngine.
- *
- */
-#include <linux/kernel.h>
-#include <asm/io.h>
-#include <mach-se/mach/se7343.h>
-
-#define badio(fn, a) panic("bad i/o operation %s for %08lx.", #fn, a)
-
-struct iop {
-       unsigned long start, end;
-       unsigned long base;
-       struct iop *(*check) (struct iop * p, unsigned long port);
-       unsigned char (*inb) (struct iop * p, unsigned long port);
-       unsigned short (*inw) (struct iop * p, unsigned long port);
-       void (*outb) (struct iop * p, unsigned char value, unsigned long port);
-       void (*outw) (struct iop * p, unsigned short value, unsigned long port);
-};
-
-struct iop *
-simple_check(struct iop *p, unsigned long port)
-{
-       static int count;
-
-       if (count < 100)
-               count++;
-
-       port &= 0xFFFF;
-
-       if ((p->start <= port) && (port <= p->end))
-               return p;
-       else
-               badio(check, port);
-}
-
-struct iop *
-ide_check(struct iop *p, unsigned long port)
-{
-       if (((0x1f0 <= port) && (port <= 0x1f7)) || (port == 0x3f7))
-               return p;
-       return NULL;
-}
-
-unsigned char
-simple_inb(struct iop *p, unsigned long port)
-{
-       return *(unsigned char *) (p->base + port);
-}
-
-unsigned short
-simple_inw(struct iop *p, unsigned long port)
-{
-       return *(unsigned short *) (p->base + port);
-}
-
-void
-simple_outb(struct iop *p, unsigned char value, unsigned long port)
-{
-       *(unsigned char *) (p->base + port) = value;
-}
-
-void
-simple_outw(struct iop *p, unsigned short value, unsigned long port)
-{
-       *(unsigned short *) (p->base + port) = value;
-}
-
-unsigned char
-pcc_inb(struct iop *p, unsigned long port)
-{
-       unsigned long addr = p->base + port + 0x40000;
-       unsigned long v;
-
-       if (port & 1)
-               addr += 0x00400000;
-       v = *(volatile unsigned char *) addr;
-       return v;
-}
-
-void
-pcc_outb(struct iop *p, unsigned char value, unsigned long port)
-{
-       unsigned long addr = p->base + port + 0x40000;
-
-       if (port & 1)
-               addr += 0x00400000;
-       *(volatile unsigned char *) addr = value;
-}
-
-unsigned char
-bad_inb(struct iop *p, unsigned long port)
-{
-       badio(inb, port);
-}
-
-void
-bad_outb(struct iop *p, unsigned char value, unsigned long port)
-{
-       badio(inw, port);
-}
-
-#ifdef CONFIG_SMC91X
-/* MSTLANEX01 LAN at 0xb400:0000 */
-static struct iop laniop = {
-       .start = 0x00,
-       .end = 0x0F,
-       .base = 0x04000000,
-       .check = simple_check,
-       .inb = simple_inb,
-       .inw = simple_inw,
-       .outb = simple_outb,
-       .outw = simple_outw,
-};
-#endif
-
-#ifdef CONFIG_NE2000
-/* NE2000 pc card NIC */
-static struct iop neiop = {
-       .start = 0x280,
-       .end = 0x29f,
-       .base = 0xb0600000 + 0x80,      /* soft 0x280 -> hard 0x300 */
-       .check = simple_check,
-       .inb = pcc_inb,
-       .inw = simple_inw,
-       .outb = pcc_outb,
-       .outw = simple_outw,
-};
-#endif
-
-#ifdef CONFIG_IDE
-/* CF in CF slot */
-static struct iop cfiop = {
-       .base = 0xb0600000,
-       .check = ide_check,
-       .inb = pcc_inb,
-       .inw = simple_inw,
-       .outb = pcc_outb,
-       .outw = simple_outw,
-};
-#endif
-
-static __inline__ struct iop *
-port2iop(unsigned long port)
-{
-       if (0) ;
-#if defined(CONFIG_SMC91X)
-       else if (laniop.check(&laniop, port))
-               return &laniop;
-#endif
-#if defined(CONFIG_NE2000)
-       else if (neiop.check(&neiop, port))
-               return &neiop;
-#endif
-#if defined(CONFIG_IDE)
-       else if (cfiop.check(&cfiop, port))
-               return &cfiop;
-#endif
-       else
-               return NULL;
-}
-
-static inline void
-delay(void)
-{
-       ctrl_inw(0xac000000);
-       ctrl_inw(0xac000000);
-}
-
-unsigned char
-sh7343se_inb(unsigned long port)
-{
-       struct iop *p = port2iop(port);
-       return (p->inb) (p, port);
-}
-
-unsigned char
-sh7343se_inb_p(unsigned long port)
-{
-       unsigned char v = sh7343se_inb(port);
-       delay();
-       return v;
-}
-
-unsigned short
-sh7343se_inw(unsigned long port)
-{
-       struct iop *p = port2iop(port);
-       return (p->inw) (p, port);
-}
-
-unsigned int
-sh7343se_inl(unsigned long port)
-{
-       badio(inl, port);
-}
-
-void
-sh7343se_outb(unsigned char value, unsigned long port)
-{
-       struct iop *p = port2iop(port);
-       (p->outb) (p, value, port);
-}
-
-void
-sh7343se_outb_p(unsigned char value, unsigned long port)
-{
-       sh7343se_outb(value, port);
-       delay();
-}
-
-void
-sh7343se_outw(unsigned short value, unsigned long port)
-{
-       struct iop *p = port2iop(port);
-       (p->outw) (p, value, port);
-}
-
-void
-sh7343se_outl(unsigned int value, unsigned long port)
-{
-       badio(outl, port);
-}
-
-void
-sh7343se_insb(unsigned long port, void *addr, unsigned long count)
-{
-       unsigned char *a = addr;
-       struct iop *p = port2iop(port);
-       while (count--)
-               *a++ = (p->inb) (p, port);
-}
-
-void
-sh7343se_insw(unsigned long port, void *addr, unsigned long count)
-{
-       unsigned short *a = addr;
-       struct iop *p = port2iop(port);
-       while (count--)
-               *a++ = (p->inw) (p, port);
-}
-
-void
-sh7343se_insl(unsigned long port, void *addr, unsigned long count)
-{
-       badio(insl, port);
-}
-
-void
-sh7343se_outsb(unsigned long port, const void *addr, unsigned long count)
-{
-       unsigned char *a = (unsigned char *) addr;
-       struct iop *p = port2iop(port);
-       while (count--)
-               (p->outb) (p, *a++, port);
-}
-
-void
-sh7343se_outsw(unsigned long port, const void *addr, unsigned long count)
-{
-       unsigned short *a = (unsigned short *) addr;
-       struct iop *p = port2iop(port);
-       while (count--)
-               (p->outw) (p, *a++, port);
-}
-
-void
-sh7343se_outsl(unsigned long port, const void *addr, unsigned long count)
-{
-       badio(outsw, port);
-}
index 486f40b..4de56f3 100644 (file)
@@ -1,36 +1,16 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/mtd/physmap.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_reg.h>
+#include <linux/usb/isp116x.h>
+#include <linux/delay.h>
 #include <asm/machvec.h>
 #include <mach-se/mach/se7343.h>
 #include <asm/heartbeat.h>
 #include <asm/irq.h>
 #include <asm/io.h>
 
-static struct resource smc91x_resources[] = {
-       [0] = {
-               .start  = 0x10000000,
-               .end    = 0x1000000F,
-               .flags  = IORESOURCE_MEM,
-       },
-       [1] = {
-               /*
-                * shared with other devices via externel
-                * interrupt controller in FPGA...
-                */
-               .start  = SMC_IRQ,
-               .end    = SMC_IRQ,
-               .flags  = IORESOURCE_IRQ,
-       },
-};
-
-static struct platform_device smc91x_device = {
-       .name           = "smc91x",
-       .id             = 0,
-       .num_resources  = ARRAY_SIZE(smc91x_resources),
-       .resource       = smc91x_resources,
-};
-
 static struct resource heartbeat_resources[] = {
        [0] = {
                .start  = PA_LED,
@@ -94,10 +74,83 @@ static struct platform_device nor_flash_device = {
        .resource       = nor_flash_resources,
 };
 
+#define ST16C2550C_FLAGS (UPF_BOOT_AUTOCONF | UPF_IOREMAP)
+
+static struct plat_serial8250_port serial_platform_data[] = {
+       [0] = {
+               .iotype         = UPIO_MEM,
+               .mapbase        = 0x16000000,
+               .regshift       = 1,
+               .flags          = ST16C2550C_FLAGS,
+               .irq            = UARTA_IRQ,
+               .uartclk        = 7372800,
+       },
+       [1] = {
+               .iotype         = UPIO_MEM,
+               .mapbase        = 0x17000000,
+               .regshift       = 1,
+               .flags          = ST16C2550C_FLAGS,
+               .irq            = UARTB_IRQ,
+               .uartclk        = 7372800,
+       },
+       { },
+};
+
+static struct platform_device uart_device = {
+       .name                   = "serial8250",
+       .id                     = PLAT8250_DEV_PLATFORM,
+       .dev                    = {
+               .platform_data  = serial_platform_data,
+       },
+};
+
+static void isp116x_delay(struct device *dev, int delay)
+{
+       ndelay(delay);
+}
+
+static struct resource usb_resources[] = {
+       [0] = {
+               .start  = 0x11800000,
+               .end    = 0x11800001,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = 0x11800002,
+               .end    = 0x11800003,
+               .flags  = IORESOURCE_MEM,
+       },
+       [2] = {
+               .start  = USB_IRQ,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct isp116x_platform_data usb_platform_data = {
+       .sel15Kres              = 1,
+       .oc_enable              = 1,
+       .int_act_high           = 0,
+       .int_edge_triggered     = 0,
+       .remote_wakeup_enable   = 0,
+       .delay                  = isp116x_delay,
+};
+
+static struct platform_device usb_device = {
+       .name                   = "isp116x-hcd",
+       .id                     = -1,
+       .num_resources          = ARRAY_SIZE(usb_resources),
+       .resource               = usb_resources,
+       .dev                    = {
+               .platform_data  = &usb_platform_data,
+       },
+
+};
+
 static struct platform_device *sh7343se_platform_devices[] __initdata = {
-       &smc91x_device,
        &heartbeat_device,
        &nor_flash_device,
+       &uart_device,
+       &usb_device,
 };
 
 static int __init sh7343se_devices_setup(void)
@@ -126,27 +179,6 @@ static void __init sh7343se_setup(char **cmdline_p)
 static struct sh_machine_vector mv_7343se __initmv = {
        .mv_name = "SolutionEngine 7343",
        .mv_setup = sh7343se_setup,
-       .mv_nr_irqs = 108,
-       .mv_inb = sh7343se_inb,
-       .mv_inw = sh7343se_inw,
-       .mv_inl = sh7343se_inl,
-       .mv_outb = sh7343se_outb,
-       .mv_outw = sh7343se_outw,
-       .mv_outl = sh7343se_outl,
-
-       .mv_inb_p = sh7343se_inb_p,
-       .mv_inw_p = sh7343se_inw,
-       .mv_inl_p = sh7343se_inl,
-       .mv_outb_p = sh7343se_outb_p,
-       .mv_outw_p = sh7343se_outw,
-       .mv_outl_p = sh7343se_outl,
-
-       .mv_insb = sh7343se_insb,
-       .mv_insw = sh7343se_insw,
-       .mv_insl = sh7343se_insl,
-       .mv_outsb = sh7343se_outsb,
-       .mv_outsw = sh7343se_outsw,
-       .mv_outsl = sh7343se_outsl,
-
+       .mv_nr_irqs = SE7343_FPGA_IRQ_BASE + SE7343_FPGA_IRQ_NR,
        .mv_init_irq = init_7343se_IRQ,
 };
index 9123d96..527eb6b 100644 (file)
@@ -8,8 +8,9 @@
  */
 #include <linux/init.h>
 #include <linux/platform_device.h>
-#include <asm/machvec.h>
 #include <mach-se/mach/se.h>
+#include <mach-se/mach/mrshpc.h>
+#include <asm/machvec.h>
 #include <asm/io.h>
 #include <asm/smc37c93x.h>
 #include <asm/heartbeat.h>
@@ -175,6 +176,7 @@ static struct platform_device *se_devices[] __initdata = {
 
 static int __init se_devices_setup(void)
 {
+       mrshpc_setup_windows();
        return platform_add_devices(se_devices, ARRAY_SIZE(se_devices));
 }
 device_initcall(se_devices_setup);
index d3fc80f..55af4c3 100644 (file)
@@ -12,8 +12,9 @@
  */
 #include <linux/init.h>
 #include <linux/platform_device.h>
-#include <asm/machvec.h>
 #include <mach-se/mach/se7721.h>
+#include <mach-se/mach/mrshpc.h>
+#include <asm/machvec.h>
 #include <asm/io.h>
 #include <asm/heartbeat.h>
 
@@ -74,8 +75,8 @@ static struct platform_device *se7721_devices[] __initdata = {
 
 static int __init se7721_devices_setup(void)
 {
-       return platform_add_devices(se7721_devices,
-               ARRAY_SIZE(se7721_devices));
+       mrshpc_setup_windows();
+       return platform_add_devices(se7721_devices, ARRAY_SIZE(se7721_devices));
 }
 device_initcall(se7721_devices_setup);
 
index fe6f965..af84904 100644 (file)
 #include <linux/ata_platform.h>
 #include <linux/input.h>
 #include <linux/smc91x.h>
+#include <mach-se/mach/se7722.h>
+#include <mach-se/mach/mrshpc.h>
 #include <asm/machvec.h>
 #include <asm/clock.h>
-#include <mach-se/mach/se7722.h>
 #include <asm/io.h>
 #include <asm/heartbeat.h>
 #include <asm/sh_keysc.h>
@@ -130,6 +131,7 @@ static struct resource sh_keysc_resources[] = {
 
 static struct platform_device sh_keysc_device = {
        .name           = "sh_keysc",
+       .id             = 0, /* "keysc0" clock */
        .num_resources  = ARRAY_SIZE(sh_keysc_resources),
        .resource       = sh_keysc_resources,
        .dev    = {
@@ -146,10 +148,8 @@ static struct platform_device *se7722_devices[] __initdata = {
 
 static int __init se7722_devices_setup(void)
 {
-       clk_always_enable("mstp214"); /* KEYSC */
-
-       return platform_add_devices(se7722_devices,
-               ARRAY_SIZE(se7722_devices));
+       mrshpc_setup_windows();
+       return platform_add_devices(se7722_devices, ARRAY_SIZE(se7722_devices));
 }
 device_initcall(se7722_devices_setup);
 
index 5771219..74cfb4b 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/irq.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
+#include <linux/ata_platform.h>
 #include <asm/io.h>
 #include <asm/rtc.h>
 #include <mach-sh03/mach/io.h>
@@ -20,19 +21,6 @@ static void __init init_sh03_IRQ(void)
        plat_irq_setup_pins(IRQ_MODE_IRQ);
 }
 
-extern void *cf_io_base;
-
-static void __iomem *sh03_ioport_map(unsigned long port, unsigned int size)
-{
-       if (PXSEG(port))
-               return (void __iomem *)port;
-       /* CompactFlash (IDE) */
-       if (((port >= 0x1f0) && (port <= 0x1f7)) || (port == 0x3f6))
-               return (void __iomem *)((unsigned long)cf_io_base + port);
-
-        return (void __iomem *)(port + PCI_IO_BASE);
-}
-
 /* arch/sh/boards/sh03/rtc.c */
 void sh03_time_init(void);
 
@@ -41,6 +29,30 @@ static void __init sh03_setup(char **cmdline_p)
        board_time_init = sh03_time_init;
 }
 
+static struct resource cf_ide_resources[] = {
+       [0] = {
+               .start  = 0x1f0,
+               .end    = 0x1f0 + 8,
+               .flags  = IORESOURCE_IO,
+       },
+       [1] = {
+               .start  = 0x1f0 + 0x206,
+               .end    = 0x1f0 +8 + 0x206 + 8,
+               .flags  = IORESOURCE_IO,
+       },
+       [2] = {
+               .start  = IRL2_IRQ,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device cf_ide_device = {
+       .name           = "pata_platform",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(cf_ide_resources),
+       .resource       = cf_ide_resources,
+};
+
 static struct resource heartbeat_resources[] = {
        [0] = {
                .start  = 0xa0800000,
@@ -58,10 +70,30 @@ static struct platform_device heartbeat_device = {
 
 static struct platform_device *sh03_devices[] __initdata = {
        &heartbeat_device,
+       &cf_ide_device,
 };
 
 static int __init sh03_devices_setup(void)
 {
+       pgprot_t prot;
+       unsigned long paddrbase;
+       void *cf_ide_base;
+
+       /* open I/O area window */
+       paddrbase = virt_to_phys((void *)PA_AREA5_IO);
+       prot = PAGE_KERNEL_PCC(1, _PAGE_PCC_IO16);
+       cf_ide_base = p3_ioremap(paddrbase, PAGE_SIZE, prot.pgprot);
+       if (!cf_ide_base) {
+               printk("allocate_cf_area : can't open CF I/O window!\n");
+               return -ENOMEM;
+       }
+
+       /* IDE cmd address : 0x1f0-0x1f7 and 0x3f6 */
+       cf_ide_resources[0].start += (unsigned long)cf_ide_base;
+       cf_ide_resources[0].end   += (unsigned long)cf_ide_base;
+       cf_ide_resources[1].start += (unsigned long)cf_ide_base;
+       cf_ide_resources[1].end   += (unsigned long)cf_ide_base;
+
        return platform_add_devices(sh03_devices, ARRAY_SIZE(sh03_devices));
 }
 __initcall(sh03_devices_setup);
@@ -70,6 +102,5 @@ static struct sh_machine_vector mv_sh03 __initmv = {
        .mv_name                = "Interface (CTP/PCI-SH03)",
        .mv_setup               = sh03_setup,
        .mv_nr_irqs             = 48,
-       .mv_ioport_map          = sh03_ioport_map,
        .mv_init_irq            = init_sh03_IRQ,
 };
index 5384068..986a0e7 100644 (file)
@@ -12,8 +12,8 @@
 #include <linux/init.h>
 #include <linux/irq.h>
 #include <linux/interrupt.h>
+#include <linux/io.h>
 
-#include <asm/io.h>
 #include <mach/systemh7751.h>
 #include <asm/smc37c93x.h>
 
@@ -24,35 +24,17 @@ static unsigned long *systemh_irq_mask_register = (unsigned long *)0xB3F10004;
 static unsigned long *systemh_irq_request_register = (unsigned long *)0xB3F10000;
 
 /* forward declaration */
-static unsigned int startup_systemh_irq(unsigned int irq);
-static void shutdown_systemh_irq(unsigned int irq);
 static void enable_systemh_irq(unsigned int irq);
 static void disable_systemh_irq(unsigned int irq);
 static void mask_and_ack_systemh(unsigned int);
-static void end_systemh_irq(unsigned int irq);
 
-/* hw_interrupt_type */
-static struct hw_interrupt_type systemh_irq_type = {
-       .typename = " SystemH Register",
-       .startup = startup_systemh_irq,
-       .shutdown = shutdown_systemh_irq,
-       .enable = enable_systemh_irq,
-       .disable = disable_systemh_irq,
+static struct irq_chip systemh_irq_type = {
+       .name = " SystemH Register",
+       .unmask = enable_systemh_irq,
+       .mask = disable_systemh_irq,
        .ack = mask_and_ack_systemh,
-       .end = end_systemh_irq
 };
 
-static unsigned int startup_systemh_irq(unsigned int irq)
-{
-       enable_systemh_irq(irq);
-       return 0; /* never anything pending */
-}
-
-static void shutdown_systemh_irq(unsigned int irq)
-{
-       disable_systemh_irq(irq);
-}
-
 static void disable_systemh_irq(unsigned int irq)
 {
        if (systemh_irq_mask_register) {
@@ -86,16 +68,9 @@ static void mask_and_ack_systemh(unsigned int irq)
        disable_systemh_irq(irq);
 }
 
-static void end_systemh_irq(unsigned int irq)
-{
-       if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
-               enable_systemh_irq(irq);
-}
-
 void make_systemh_irq(unsigned int irq)
 {
        disable_irq_nosync(irq);
-       irq_desc[irq].chip = &systemh_irq_type;
+       set_irq_chip_and_handler(irq, &systemh_irq_type, handle_level_irq);
        disable_systemh_irq(irq);
 }
-
index f1a4a07..27ceeb9 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/irq.h>
-#include <asm/io.h>
+#include <linux/io.h>
 #include <asm/irq.h>
 #include <asm/hd64461.h>
 
 /* This belongs in cpu specific */
 #define INTC_ICR1 0xA4140010UL
 
-static void disable_hd64461_irq(unsigned int irq)
+static void hd64461_mask_irq(unsigned int irq)
 {
        unsigned short nimr;
        unsigned short mask = 1 << (irq - HD64461_IRQBASE);
 
-       nimr = inw(HD64461_NIMR);
+       nimr = __raw_readw(HD64461_NIMR);
        nimr |= mask;
-       outw(nimr, HD64461_NIMR);
+       __raw_writew(nimr, HD64461_NIMR);
 }
 
-static void enable_hd64461_irq(unsigned int irq)
+static void hd64461_unmask_irq(unsigned int irq)
 {
        unsigned short nimr;
        unsigned short mask = 1 << (irq - HD64461_IRQBASE);
 
-       nimr = inw(HD64461_NIMR);
+       nimr = __raw_readw(HD64461_NIMR);
        nimr &= ~mask;
-       outw(nimr, HD64461_NIMR);
+       __raw_writew(nimr, HD64461_NIMR);
 }
 
-static void mask_and_ack_hd64461(unsigned int irq)
+static void hd64461_mask_and_ack_irq(unsigned int irq)
 {
-       disable_hd64461_irq(irq);
+       hd64461_mask_irq(irq);
 #ifdef CONFIG_HD64461_ENABLER
        if (irq == HD64461_IRQBASE + 13)
-               outb(0x00, HD64461_PCC1CSCR);
+               __raw_writeb(0x00, HD64461_PCC1CSCR);
 #endif
 }
 
-static void end_hd64461_irq(unsigned int irq)
-{
-       if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
-               enable_hd64461_irq(irq);
-}
-
-static unsigned int startup_hd64461_irq(unsigned int irq)
-{
-       enable_hd64461_irq(irq);
-       return 0;
-}
-
-static void shutdown_hd64461_irq(unsigned int irq)
-{
-       disable_hd64461_irq(irq);
-}
-
-static struct hw_interrupt_type hd64461_irq_type = {
-       .typename       = "HD64461-IRQ",
-       .startup        = startup_hd64461_irq,
-       .shutdown       = shutdown_hd64461_irq,
-       .enable         = enable_hd64461_irq,
-       .disable        = disable_hd64461_irq,
-       .ack            = mask_and_ack_hd64461,
-       .end            = end_hd64461_irq,
+static struct irq_chip hd64461_irq_chip = {
+       .name           = "HD64461-IRQ",
+       .mask           = hd64461_mask_irq,
+       .mask_ack       = hd64461_mask_and_ack_irq,
+       .unmask         = hd64461_unmask_irq,
 };
 
-static irqreturn_t hd64461_interrupt(int irq, void *dev_id)
-{
-       printk(KERN_INFO
-              "HD64461: spurious interrupt, nirr: 0x%x nimr: 0x%x\n",
-              inw(HD64461_NIRR), inw(HD64461_NIMR));
-
-       return IRQ_NONE;
-}
-
-static struct {
-       int (*func) (int, void *);
-       void *dev;
-} hd64461_demux[HD64461_IRQ_NUM];
-
-void hd64461_register_irq_demux(int irq,
-                               int (*demux) (int irq, void *dev), void *dev)
-{
-       hd64461_demux[irq - HD64461_IRQBASE].func = demux;
-       hd64461_demux[irq - HD64461_IRQBASE].dev = dev;
-}
-
-EXPORT_SYMBOL(hd64461_register_irq_demux);
-
-void hd64461_unregister_irq_demux(int irq)
-{
-       hd64461_demux[irq - HD64461_IRQBASE].func = 0;
-}
-
-EXPORT_SYMBOL(hd64461_unregister_irq_demux);
-
 int hd64461_irq_demux(int irq)
 {
        if (irq == CONFIG_HD64461_IRQ) {
@@ -115,25 +65,11 @@ int hd64461_irq_demux(int irq)
                for (bit = 1, i = 0; i < 16; bit <<= 1, i++)
                        if (nirr & bit)
                                break;
-               if (i == 16)
-                       irq = CONFIG_HD64461_IRQ;
-               else {
-                       irq = HD64461_IRQBASE + i;
-                       if (hd64461_demux[i].func != 0) {
-                               irq = hd64461_demux[i].func(irq, hd64461_demux[i].dev);
-                       }
-               }
+               irq = HD64461_IRQBASE + i;
        }
        return irq;
 }
 
-static struct irqaction irq0 = {
-       .handler = hd64461_interrupt,
-       .flags = IRQF_DISABLED,
-       .mask = CPU_MASK_NONE,
-       .name = "HD64461",
-};
-
 int __init setup_hd64461(void)
 {
        int i;
@@ -146,22 +82,21 @@ int __init setup_hd64461(void)
               CONFIG_HD64461_IOBASE, CONFIG_HD64461_IRQ, HD64461_IRQBASE,
               HD64461_IRQBASE + 15);
 
-#if defined(CONFIG_CPU_SUBTYPE_SH7709) /* Should be at processor specific part.. */
-       outw(0x2240, INTC_ICR1);
+/* Should be at processor specific part.. */
+#if defined(CONFIG_CPU_SUBTYPE_SH7709)
+       __raw_writew(0x2240, INTC_ICR1);
 #endif
-       outw(0xffff, HD64461_NIMR);
+       __raw_writew(0xffff, HD64461_NIMR);
 
        /*  IRQ 80 -> 95 belongs to HD64461  */
-       for (i = HD64461_IRQBASE; i < HD64461_IRQBASE + 16; i++) {
-               irq_desc[i].chip = &hd64461_irq_type;
-       }
-
-       setup_irq(CONFIG_HD64461_IRQ, &irq0);
+       for (i = HD64461_IRQBASE; i < HD64461_IRQBASE + 16; i++)
+               set_irq_chip_and_handler(i, &hd64461_irq_chip,
+                                        handle_level_irq);
 
 #ifdef CONFIG_HD64461_ENABLER
        printk(KERN_INFO "HD64461: enabling PCMCIA devices\n");
-       outb(0x4c, HD64461_PCC1CSCIER);
-       outb(0x00, HD64461_PCC1CSCR);
+       __raw_writeb(0x4c, HD64461_PCC1CSCIER);
+       __raw_writeb(0x00, HD64461_PCC1CSCR);
 #endif
 
        return 0;
diff --git a/arch/sh/configs/edosk7705_defconfig b/arch/sh/configs/edosk7705_defconfig
new file mode 100644 (file)
index 0000000..8f4329f
--- /dev/null
@@ -0,0 +1,438 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.28-rc6
+# Wed Dec 17 13:53:02 2008
+#
+CONFIG_SUPERH=y
+CONFIG_SUPERH32=y
+CONFIG_ARCH_DEFCONFIG="arch/sh/configs/shx3_defconfig"
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_FIND_NEXT_BIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_GENERIC_IRQ_PROBE=y
+# CONFIG_GENERIC_GPIO is not set
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_ARCH_SUSPEND_POSSIBLE is not set
+# CONFIG_ARCH_HIBERNATION_POSSIBLE is not set
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_ARCH_NO_VIRT_TO_BUS=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+# CONFIG_EXPERIMENTAL is not set
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SYSVIPC is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=17
+# CONFIG_CGROUPS is not set
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_EMBEDDED=y
+# CONFIG_UID16 is not set
+# CONFIG_SYSCTL_SYSCALL is not set
+# CONFIG_KALLSYMS is not set
+# CONFIG_HOTPLUG is not set
+# CONFIG_PRINTK is not set
+# CONFIG_BUG is not set
+# CONFIG_ELF_CORE is not set
+# CONFIG_COMPAT_BRK is not set
+# CONFIG_BASE_FULL is not set
+# CONFIG_FUTEX is not set
+# CONFIG_EPOLL is not set
+# CONFIG_SIGNALFD is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_EVENTFD is not set
+CONFIG_SHMEM=y
+# CONFIG_AIO is not set
+# CONFIG_VM_EVENT_COUNTERS is not set
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+CONFIG_HAVE_IOREMAP_PROT=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=1
+# CONFIG_MODULES is not set
+# CONFIG_BLOCK is not set
+CONFIG_CLASSIC_RCU=y
+# CONFIG_FREEZER is not set
+
+#
+# System type
+#
+CONFIG_CPU_SH3=y
+# CONFIG_CPU_SUBTYPE_SH7619 is not set
+# CONFIG_CPU_SUBTYPE_SH7201 is not set
+# CONFIG_CPU_SUBTYPE_SH7203 is not set
+# CONFIG_CPU_SUBTYPE_SH7206 is not set
+# CONFIG_CPU_SUBTYPE_SH7263 is not set
+# CONFIG_CPU_SUBTYPE_MXG is not set
+CONFIG_CPU_SUBTYPE_SH7705=y
+# CONFIG_CPU_SUBTYPE_SH7706 is not set
+# CONFIG_CPU_SUBTYPE_SH7707 is not set
+# CONFIG_CPU_SUBTYPE_SH7708 is not set
+# CONFIG_CPU_SUBTYPE_SH7709 is not set
+# CONFIG_CPU_SUBTYPE_SH7710 is not set
+# CONFIG_CPU_SUBTYPE_SH7712 is not set
+# CONFIG_CPU_SUBTYPE_SH7720 is not set
+# CONFIG_CPU_SUBTYPE_SH7721 is not set
+# CONFIG_CPU_SUBTYPE_SH7750 is not set
+# CONFIG_CPU_SUBTYPE_SH7091 is not set
+# CONFIG_CPU_SUBTYPE_SH7750R is not set
+# CONFIG_CPU_SUBTYPE_SH7750S is not set
+# CONFIG_CPU_SUBTYPE_SH7751 is not set
+# CONFIG_CPU_SUBTYPE_SH7751R is not set
+# CONFIG_CPU_SUBTYPE_SH7760 is not set
+# CONFIG_CPU_SUBTYPE_SH4_202 is not set
+# CONFIG_CPU_SUBTYPE_SH7723 is not set
+# CONFIG_CPU_SUBTYPE_SH7763 is not set
+# CONFIG_CPU_SUBTYPE_SH7770 is not set
+# CONFIG_CPU_SUBTYPE_SH7780 is not set
+# CONFIG_CPU_SUBTYPE_SH7785 is not set
+# CONFIG_CPU_SUBTYPE_SHX3 is not set
+# CONFIG_CPU_SUBTYPE_SH7343 is not set
+# CONFIG_CPU_SUBTYPE_SH7722 is not set
+# CONFIG_CPU_SUBTYPE_SH7366 is not set
+# CONFIG_CPU_SUBTYPE_SH5_101 is not set
+# CONFIG_CPU_SUBTYPE_SH5_103 is not set
+
+#
+# Memory management options
+#
+CONFIG_QUICKLIST=y
+CONFIG_MMU=y
+CONFIG_PAGE_OFFSET=0x80000000
+CONFIG_MEMORY_START=0x08000000
+CONFIG_MEMORY_SIZE=0x04000000
+CONFIG_29BIT=y
+CONFIG_VSYSCALL=y
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_DEFAULT=y
+CONFIG_MAX_ACTIVE_REGIONS=1
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_8KB is not set
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_64KB is not set
+CONFIG_ENTRY_OFFSET=0x00001000
+CONFIG_SELECT_MEMORY_MODEL=y
+# CONFIG_FLATMEM_MANUAL is not set
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+CONFIG_SPARSEMEM_MANUAL=y
+CONFIG_SPARSEMEM=y
+CONFIG_HAVE_MEMORY_PRESENT=y
+CONFIG_SPARSEMEM_STATIC=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+CONFIG_MIGRATION=y
+# CONFIG_RESOURCES_64BIT is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_NR_QUICK=2
+CONFIG_UNEVICTABLE_LRU=y
+
+#
+# Cache configuration
+#
+CONFIG_SH7705_CACHE_32KB=y
+# CONFIG_SH_DIRECT_MAPPED is not set
+CONFIG_CACHE_WRITEBACK=y
+# CONFIG_CACHE_WRITETHROUGH is not set
+# CONFIG_CACHE_OFF is not set
+
+#
+# Processor features
+#
+CONFIG_CPU_LITTLE_ENDIAN=y
+# CONFIG_CPU_BIG_ENDIAN is not set
+CONFIG_SH_ADC=y
+CONFIG_CPU_HAS_INTEVT=y
+CONFIG_CPU_HAS_SR_RB=y
+
+#
+# Board support
+#
+# CONFIG_SH_SOLUTION_ENGINE is not set
+CONFIG_SH_EDOSK7705=y
+
+#
+# Timer and clock configuration
+#
+CONFIG_SH_TMU=y
+CONFIG_SH_TIMER_IRQ=16
+CONFIG_SH_PCLK_FREQ=31250000
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# DMA support
+#
+# CONFIG_SH_DMA is not set
+
+#
+# Companion Chips
+#
+
+#
+# Additional SuperH Device Drivers
+#
+# CONFIG_HEARTBEAT is not set
+# CONFIG_PUSH_SWITCH is not set
+
+#
+# Kernel features
+#
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+# CONFIG_SCHED_HRTICK is not set
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_GUSA=y
+# CONFIG_GUSA_RB is not set
+
+#
+# Boot options
+#
+CONFIG_ZERO_PAGE_OFFSET=0x00001000
+CONFIG_BOOT_LINK_OFFSET=0x00800000
+# CONFIG_CMDLINE_BOOL is not set
+
+#
+# Bus options
+#
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_HAVE_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_NET is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_MTD is not set
+# CONFIG_PARPORT is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+
+#
+# SCSI device support
+#
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+# CONFIG_INPUT is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_DEVKMEM is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_SH_SCI is not set
+# CONFIG_UNIX98_PTYS is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_REGULATOR is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+# CONFIG_SOUND is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+CONFIG_STAGING_EXCLUDE_BUILD=y
+
+#
+# File systems
+#
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# Pseudo filesystems
+#
+# CONFIG_PROC_FS is not set
+# CONFIG_SYSFS is not set
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_LATENCYTOP is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+
+#
+# Tracers
+#
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_SH_STANDARD_BIOS is not set
+# CONFIG_EARLY_SCIF_CONSOLE is not set
+# CONFIG_MORE_COMPILE_OPTIONS is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+# CONFIG_CRC32 is not set
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/sh/configs/rsk7201_defconfig b/arch/sh/configs/rsk7201_defconfig
new file mode 100644 (file)
index 0000000..014c18c
--- /dev/null
@@ -0,0 +1,703 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.28-rc6
+# Mon Dec  8 14:48:02 2008
+#
+CONFIG_SUPERH=y
+CONFIG_SUPERH32=y
+CONFIG_ARCH_DEFCONFIG="arch/sh/configs/shx3_defconfig"
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_FIND_NEXT_BIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_GENERIC_IRQ_PROBE=y
+# CONFIG_GENERIC_GPIO is not set
+# CONFIG_GENERIC_TIME is not set
+# CONFIG_GENERIC_CLOCKEVENTS is not set
+# CONFIG_ARCH_SUSPEND_POSSIBLE is not set
+# CONFIG_ARCH_HIBERNATION_POSSIBLE is not set
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_ARCH_NO_VIRT_TO_BUS=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_IKCONFIG=y
+# CONFIG_IKCONFIG_PROC is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED is not set
+# CONFIG_SYSFS_DEPRECATED_V2 is not set
+# CONFIG_RELAY is not set
+CONFIG_NAMESPACES=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+CONFIG_USER_NS=y
+CONFIG_PID_NS=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+# CONFIG_AIO is not set
+CONFIG_VM_EVENT_COUNTERS=y
+# CONFIG_SLAB is not set
+# CONFIG_SLUB is not set
+CONFIG_SLOB=y
+CONFIG_PROFILING=y
+# CONFIG_MARKERS is not set
+CONFIG_OPROFILE=y
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_RT_MUTEXES=y
+CONFIG_TINY_SHMEM=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+# CONFIG_MODULE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+CONFIG_DEFAULT_NOOP=y
+CONFIG_DEFAULT_IOSCHED="noop"
+CONFIG_CLASSIC_RCU=y
+# CONFIG_FREEZER is not set
+
+#
+# System type
+#
+CONFIG_CPU_SH2=y
+CONFIG_CPU_SH2A=y
+# CONFIG_CPU_SUBTYPE_SH7619 is not set
+CONFIG_CPU_SUBTYPE_SH7201=y
+# CONFIG_CPU_SUBTYPE_SH7203 is not set
+# CONFIG_CPU_SUBTYPE_SH7206 is not set
+# CONFIG_CPU_SUBTYPE_SH7263 is not set
+# CONFIG_CPU_SUBTYPE_MXG is not set
+# CONFIG_CPU_SUBTYPE_SH7705 is not set
+# CONFIG_CPU_SUBTYPE_SH7706 is not set
+# CONFIG_CPU_SUBTYPE_SH7707 is not set
+# CONFIG_CPU_SUBTYPE_SH7708 is not set
+# CONFIG_CPU_SUBTYPE_SH7709 is not set
+# CONFIG_CPU_SUBTYPE_SH7710 is not set
+# CONFIG_CPU_SUBTYPE_SH7712 is not set
+# CONFIG_CPU_SUBTYPE_SH7720 is not set
+# CONFIG_CPU_SUBTYPE_SH7721 is not set
+# CONFIG_CPU_SUBTYPE_SH7750 is not set
+# CONFIG_CPU_SUBTYPE_SH7091 is not set
+# CONFIG_CPU_SUBTYPE_SH7750R is not set
+# CONFIG_CPU_SUBTYPE_SH7750S is not set
+# CONFIG_CPU_SUBTYPE_SH7751 is not set
+# CONFIG_CPU_SUBTYPE_SH7751R is not set
+# CONFIG_CPU_SUBTYPE_SH7760 is not set
+# CONFIG_CPU_SUBTYPE_SH4_202 is not set
+# CONFIG_CPU_SUBTYPE_SH7723 is not set
+# CONFIG_CPU_SUBTYPE_SH7763 is not set
+# CONFIG_CPU_SUBTYPE_SH7770 is not set
+# CONFIG_CPU_SUBTYPE_SH7780 is not set
+# CONFIG_CPU_SUBTYPE_SH7785 is not set
+# CONFIG_CPU_SUBTYPE_SHX3 is not set
+# CONFIG_CPU_SUBTYPE_SH7343 is not set
+# CONFIG_CPU_SUBTYPE_SH7722 is not set
+# CONFIG_CPU_SUBTYPE_SH7366 is not set
+# CONFIG_CPU_SUBTYPE_SH5_101 is not set
+# CONFIG_CPU_SUBTYPE_SH5_103 is not set
+
+#
+# Memory management options
+#
+CONFIG_QUICKLIST=y
+CONFIG_PAGE_OFFSET=0x00000000
+CONFIG_MEMORY_START=0x08000000
+CONFIG_MEMORY_SIZE=0x01000000
+CONFIG_29BIT=y
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_DEFAULT=y
+CONFIG_MAX_ACTIVE_REGIONS=1
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_8KB is not set
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_64KB is not set
+CONFIG_ENTRY_OFFSET=0x00001000
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_SPARSEMEM_STATIC=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_NR_QUICK=2
+
+#
+# Cache configuration
+#
+# CONFIG_SH_DIRECT_MAPPED is not set
+CONFIG_CACHE_WRITEBACK=y
+# CONFIG_CACHE_WRITETHROUGH is not set
+# CONFIG_CACHE_OFF is not set
+
+#
+# Processor features
+#
+# CONFIG_CPU_LITTLE_ENDIAN is not set
+CONFIG_CPU_BIG_ENDIAN=y
+CONFIG_SH_FPU=y
+CONFIG_CPU_HAS_FPU=y
+
+#
+# Board support
+#
+CONFIG_SH_RSK=y
+CONFIG_SH_RSK7201=y
+# CONFIG_SH_RSK7203 is not set
+
+#
+# Timer and clock configuration
+#
+# CONFIG_SH_CMT is not set
+CONFIG_SH_MTU2=y
+CONFIG_SH_TIMER_IRQ=16
+CONFIG_SH_PCLK_FREQ=40000000
+CONFIG_SH_CLK_MD=0
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# DMA support
+#
+
+#
+# Companion Chips
+#
+
+#
+# Additional SuperH Device Drivers
+#
+# CONFIG_HEARTBEAT is not set
+# CONFIG_PUSH_SWITCH is not set
+
+#
+# Kernel features
+#
+# CONFIG_HZ_100 is not set
+# CONFIG_HZ_250 is not set
+# CONFIG_HZ_300 is not set
+CONFIG_HZ_1000=y
+CONFIG_HZ=1000
+# CONFIG_SCHED_HRTICK is not set
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+# CONFIG_SECCOMP is not set
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_GUSA=y
+
+#
+# Boot options
+#
+CONFIG_ZERO_PAGE_OFFSET=0x00001000
+CONFIG_BOOT_LINK_OFFSET=0x00800000
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttySC0,115200 earlyprintk=serial ignore_loglevel"
+
+#
+# Bus options
+#
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF_FDPIC=y
+CONFIG_BINFMT_FLAT=y
+CONFIG_BINFMT_ZFLAT=y
+CONFIG_BINFMT_SHARED_FLAT=y
+# CONFIG_HAVE_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options (EXPERIMENTAL)
+#
+CONFIG_PM=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+# CONFIG_NET is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_STANDALONE is not set
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_FW_LOADER is not set
+# CONFIG_SYS_HYPERVISOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+CONFIG_MTD_CONCAT=y
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_REDBOOT_PARTS=y
+CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS 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_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_START=0x0
+CONFIG_MTD_PHYSMAP_LEN=0x0
+CONFIG_MTD_PHYSMAP_BANKWIDTH=4
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_C2PORT is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_NR_UARTS=8
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_UNIX98_PTYS is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+CONFIG_THERMAL=y
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_REGULATOR is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+CONFIG_DAB=y
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=y
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_DRV_SH=y
+# CONFIG_DMADEVICES is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+CONFIG_STAGING_EXCLUDE_BUILD=y
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_FILE_LOCKING is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+# CONFIG_JFFS2_SUMMARY is not set
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+CONFIG_ROMFS_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=1024
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_LATENCYTOP is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+
+#
+# Tracers
+#
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
+# CONFIG_SAMPLES is not set
+# CONFIG_SH_STANDARD_BIOS is not set
+# CONFIG_EARLY_SCIF_CONSOLE is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
index 85b0ac4..dcdef31 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.27
-# Tue Oct 21 12:58:47 2008
+# Linux kernel version: 2.6.28-rc6
+# Mon Dec  8 14:35:03 2008
 #
 CONFIG_SUPERH=y
 CONFIG_SUPERH32=y
@@ -16,6 +16,8 @@ CONFIG_GENERIC_IRQ_PROBE=y
 CONFIG_GENERIC_GPIO=y
 # CONFIG_GENERIC_TIME is not set
 # CONFIG_GENERIC_CLOCKEVENTS is not set
+# CONFIG_ARCH_SUSPEND_POSSIBLE is not set
+# CONFIG_ARCH_HIBERNATION_POSSIBLE is not set
 CONFIG_STACKTRACE_SUPPORT=y
 CONFIG_LOCKDEP_SUPPORT=y
 CONFIG_HAVE_LATENCYTOP_SUPPORT=y
@@ -75,7 +77,6 @@ CONFIG_TIMERFD=y
 CONFIG_EVENTFD=y
 CONFIG_AIO=y
 CONFIG_VM_EVENT_COUNTERS=y
-CONFIG_PCI_QUIRKS=y
 # CONFIG_SLAB is not set
 # CONFIG_SLUB is not set
 CONFIG_SLOB=y
@@ -126,6 +127,7 @@ CONFIG_CLASSIC_RCU=y
 CONFIG_CPU_SH2=y
 CONFIG_CPU_SH2A=y
 # CONFIG_CPU_SUBTYPE_SH7619 is not set
+# CONFIG_CPU_SUBTYPE_SH7201 is not set
 CONFIG_CPU_SUBTYPE_SH7203=y
 # CONFIG_CPU_SUBTYPE_SH7206 is not set
 # CONFIG_CPU_SUBTYPE_SH7263 is not set
@@ -211,6 +213,8 @@ CONFIG_CPU_HAS_FPU=y
 #
 # Board support
 #
+CONFIG_SH_RSK=y
+# CONFIG_SH_RSK7201 is not set
 CONFIG_SH_RSK7203=y
 
 #
@@ -296,6 +300,14 @@ CONFIG_BINFMT_ZFLAT=y
 CONFIG_BINFMT_SHARED_FLAT=y
 # CONFIG_HAVE_AOUT is not set
 # CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options (EXPERIMENTAL)
+#
+CONFIG_PM=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
 CONFIG_NET=y
 
 #
@@ -477,6 +489,7 @@ CONFIG_BLK_DEV=y
 CONFIG_MISC_DEVICES=y
 # CONFIG_EEPROM_93CX6 is not set
 # CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_C2PORT is not set
 CONFIG_HAVE_IDE=y
 # CONFIG_IDE is not set
 
@@ -603,11 +616,11 @@ CONFIG_SERIAL_CORE_CONSOLE=y
 # CONFIG_HWMON is not set
 CONFIG_THERMAL=y
 # CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
 
 #
 # Sonics Silicon Backplane
 #
-CONFIG_SSB_POSSIBLE=y
 # CONFIG_SSB is not set
 
 #
@@ -617,7 +630,11 @@ CONFIG_SSB_POSSIBLE=y
 # CONFIG_MFD_SM501 is not set
 # CONFIG_HTC_PASIC3 is not set
 # CONFIG_MFD_TMIO is not set
-# CONFIG_MFD_WM8400 is not set
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
 
 #
 # Multimedia devices
@@ -702,19 +719,22 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
 CONFIG_USB_DEVICEFS=y
 CONFIG_USB_DEVICE_CLASS=y
 # CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_SUSPEND is not set
 # CONFIG_USB_OTG is not set
 # CONFIG_USB_OTG_WHITELIST is not set
 # CONFIG_USB_OTG_BLACKLIST_HUB is not set
 CONFIG_USB_MON=y
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
 
 #
 # USB Host Controller Drivers
 #
 # CONFIG_USB_C67X00_HCD is not set
 # CONFIG_USB_ISP116X_HCD is not set
-# CONFIG_USB_ISP1760_HCD is not set
 # CONFIG_USB_SL811_HCD is not set
 CONFIG_USB_R8A66597_HCD=y
+# CONFIG_USB_HWA_HCD is not set
 
 #
 # USB Device Class drivers
@@ -725,11 +745,11 @@ CONFIG_USB_R8A66597_HCD=y
 # CONFIG_USB_TMC is not set
 
 #
-# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed;
 #
 
 #
-# may also be needed; see USB_STORAGE Help for more information
+# see USB_STORAGE Help for more information
 #
 # CONFIG_USB_LIBUSUAL is not set
 
@@ -770,7 +790,22 @@ CONFIG_USB_R8A66597_HCD=y
 # CONFIG_USB_GADGET is not set
 # CONFIG_MMC is not set
 # CONFIG_MEMSTICK is not set
-# CONFIG_NEW_LEDS is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+CONFIG_LEDS_GPIO=y
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
 # CONFIG_ACCESSIBILITY is not set
 CONFIG_RTC_LIB=y
 CONFIG_RTC_CLASS=y
@@ -812,6 +847,7 @@ CONFIG_RTC_DRV_SH=y
 # CONFIG_DMADEVICES is not set
 # CONFIG_UIO is not set
 # CONFIG_STAGING is not set
+CONFIG_STAGING_EXCLUDE_BUILD=y
 
 #
 # File systems
@@ -950,9 +986,14 @@ CONFIG_FRAME_POINTER=y
 # CONFIG_FAULT_INJECTION is not set
 # CONFIG_LATENCYTOP is not set
 CONFIG_SYSCTL_SYSCALL_CHECK=y
-CONFIG_NOP_TRACER=y
-CONFIG_HAVE_FTRACE=y
-# CONFIG_FTRACE is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+
+#
+# Tracers
+#
+# CONFIG_FUNCTION_TRACER is not set
 # CONFIG_SCHED_TRACER is not set
 # CONFIG_CONTEXT_SWITCH_TRACER is not set
 # CONFIG_BOOT_TRACER is not set
diff --git a/arch/sh/configs/rts7751r2dplus_qemu_defconfig b/arch/sh/configs/rts7751r2dplus_qemu_defconfig
deleted file mode 100644 (file)
index ae8f630..0000000
+++ /dev/null
@@ -1,949 +0,0 @@
-#
-# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.27
-# Wed Oct 22 18:51:20 2008
-#
-CONFIG_SUPERH=y
-CONFIG_SUPERH32=y
-CONFIG_ARCH_DEFCONFIG="arch/sh/configs/shx3_defconfig"
-CONFIG_RWSEM_GENERIC_SPINLOCK=y
-CONFIG_GENERIC_BUG=y
-CONFIG_GENERIC_FIND_NEXT_BIT=y
-CONFIG_GENERIC_HWEIGHT=y
-CONFIG_GENERIC_HARDIRQS=y
-CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
-CONFIG_GENERIC_IRQ_PROBE=y
-# CONFIG_GENERIC_GPIO is not set
-CONFIG_GENERIC_TIME=y
-CONFIG_GENERIC_CLOCKEVENTS=y
-CONFIG_SYS_SUPPORTS_PCI=y
-CONFIG_STACKTRACE_SUPPORT=y
-CONFIG_LOCKDEP_SUPPORT=y
-CONFIG_HAVE_LATENCYTOP_SUPPORT=y
-# CONFIG_ARCH_HAS_ILOG2_U32 is not set
-# CONFIG_ARCH_HAS_ILOG2_U64 is not set
-CONFIG_ARCH_NO_VIRT_TO_BUS=y
-CONFIG_IO_TRAPPED=y
-CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
-
-#
-# General setup
-#
-CONFIG_EXPERIMENTAL=y
-CONFIG_BROKEN_ON_SMP=y
-CONFIG_INIT_ENV_ARG_LIMIT=32
-CONFIG_LOCALVERSION=""
-CONFIG_LOCALVERSION_AUTO=y
-CONFIG_SWAP=y
-CONFIG_SYSVIPC=y
-CONFIG_SYSVIPC_SYSCTL=y
-# CONFIG_BSD_PROCESS_ACCT is not set
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_LOG_BUF_SHIFT=14
-# CONFIG_CGROUPS is not set
-CONFIG_GROUP_SCHED=y
-CONFIG_FAIR_GROUP_SCHED=y
-# CONFIG_RT_GROUP_SCHED is not set
-CONFIG_USER_SCHED=y
-# CONFIG_CGROUP_SCHED is not set
-CONFIG_SYSFS_DEPRECATED=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-# CONFIG_RELAY is not set
-# CONFIG_NAMESPACES is not set
-CONFIG_BLK_DEV_INITRD=y
-CONFIG_INITRAMFS_SOURCE=""
-# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
-CONFIG_SYSCTL=y
-CONFIG_EMBEDDED=y
-CONFIG_UID16=y
-# CONFIG_SYSCTL_SYSCALL is not set
-CONFIG_KALLSYMS=y
-# CONFIG_KALLSYMS_ALL is not set
-# CONFIG_KALLSYMS_EXTRA_PASS is not set
-# CONFIG_HOTPLUG is not set
-CONFIG_PRINTK=y
-CONFIG_BUG=y
-CONFIG_ELF_CORE=y
-CONFIG_COMPAT_BRK=y
-CONFIG_BASE_FULL=y
-CONFIG_FUTEX=y
-CONFIG_ANON_INODES=y
-CONFIG_EPOLL=y
-CONFIG_SIGNALFD=y
-CONFIG_TIMERFD=y
-CONFIG_EVENTFD=y
-CONFIG_SHMEM=y
-CONFIG_AIO=y
-CONFIG_VM_EVENT_COUNTERS=y
-CONFIG_PCI_QUIRKS=y
-CONFIG_SLAB=y
-# CONFIG_SLUB is not set
-# CONFIG_SLOB is not set
-CONFIG_PROFILING=y
-# CONFIG_MARKERS is not set
-CONFIG_OPROFILE=y
-CONFIG_HAVE_OPROFILE=y
-# CONFIG_KPROBES is not set
-CONFIG_HAVE_IOREMAP_PROT=y
-CONFIG_HAVE_KPROBES=y
-CONFIG_HAVE_KRETPROBES=y
-CONFIG_HAVE_ARCH_TRACEHOOK=y
-CONFIG_HAVE_CLK=y
-CONFIG_HAVE_GENERIC_DMA_COHERENT=y
-CONFIG_SLABINFO=y
-CONFIG_RT_MUTEXES=y
-# CONFIG_TINY_SHMEM is not set
-CONFIG_BASE_SMALL=0
-CONFIG_MODULES=y
-# CONFIG_MODULE_FORCE_LOAD is not set
-# CONFIG_MODULE_UNLOAD is not set
-# CONFIG_MODVERSIONS is not set
-# CONFIG_MODULE_SRCVERSION_ALL is not set
-CONFIG_KMOD=y
-CONFIG_BLOCK=y
-# CONFIG_LBD is not set
-# CONFIG_BLK_DEV_IO_TRACE is not set
-# CONFIG_LSF is not set
-# CONFIG_BLK_DEV_BSG is not set
-# CONFIG_BLK_DEV_INTEGRITY is not set
-
-#
-# IO Schedulers
-#
-CONFIG_IOSCHED_NOOP=y
-CONFIG_IOSCHED_AS=y
-CONFIG_IOSCHED_DEADLINE=y
-CONFIG_IOSCHED_CFQ=y
-CONFIG_DEFAULT_AS=y
-# CONFIG_DEFAULT_DEADLINE is not set
-# CONFIG_DEFAULT_CFQ is not set
-# CONFIG_DEFAULT_NOOP is not set
-CONFIG_DEFAULT_IOSCHED="anticipatory"
-CONFIG_CLASSIC_RCU=y
-# CONFIG_FREEZER is not set
-
-#
-# System type
-#
-CONFIG_CPU_SH4=y
-# CONFIG_CPU_SUBTYPE_SH7619 is not set
-# CONFIG_CPU_SUBTYPE_SH7203 is not set
-# CONFIG_CPU_SUBTYPE_SH7206 is not set
-# CONFIG_CPU_SUBTYPE_SH7263 is not set
-# CONFIG_CPU_SUBTYPE_MXG is not set
-# CONFIG_CPU_SUBTYPE_SH7705 is not set
-# CONFIG_CPU_SUBTYPE_SH7706 is not set
-# CONFIG_CPU_SUBTYPE_SH7707 is not set
-# CONFIG_CPU_SUBTYPE_SH7708 is not set
-# CONFIG_CPU_SUBTYPE_SH7709 is not set
-# CONFIG_CPU_SUBTYPE_SH7710 is not set
-# CONFIG_CPU_SUBTYPE_SH7712 is not set
-# CONFIG_CPU_SUBTYPE_SH7720 is not set
-# CONFIG_CPU_SUBTYPE_SH7721 is not set
-# CONFIG_CPU_SUBTYPE_SH7750 is not set
-# CONFIG_CPU_SUBTYPE_SH7091 is not set
-# CONFIG_CPU_SUBTYPE_SH7750R is not set
-# CONFIG_CPU_SUBTYPE_SH7750S is not set
-# CONFIG_CPU_SUBTYPE_SH7751 is not set
-CONFIG_CPU_SUBTYPE_SH7751R=y
-# CONFIG_CPU_SUBTYPE_SH7760 is not set
-# CONFIG_CPU_SUBTYPE_SH4_202 is not set
-# CONFIG_CPU_SUBTYPE_SH7723 is not set
-# CONFIG_CPU_SUBTYPE_SH7763 is not set
-# CONFIG_CPU_SUBTYPE_SH7770 is not set
-# CONFIG_CPU_SUBTYPE_SH7780 is not set
-# CONFIG_CPU_SUBTYPE_SH7785 is not set
-# CONFIG_CPU_SUBTYPE_SHX3 is not set
-# CONFIG_CPU_SUBTYPE_SH7343 is not set
-# CONFIG_CPU_SUBTYPE_SH7722 is not set
-# CONFIG_CPU_SUBTYPE_SH7366 is not set
-# CONFIG_CPU_SUBTYPE_SH5_101 is not set
-# CONFIG_CPU_SUBTYPE_SH5_103 is not set
-
-#
-# Memory management options
-#
-CONFIG_QUICKLIST=y
-CONFIG_MMU=y
-CONFIG_PAGE_OFFSET=0x80000000
-CONFIG_MEMORY_START=0x0c000000
-CONFIG_MEMORY_SIZE=0x04000000
-CONFIG_29BIT=y
-CONFIG_VSYSCALL=y
-CONFIG_ARCH_FLATMEM_ENABLE=y
-CONFIG_ARCH_SPARSEMEM_ENABLE=y
-CONFIG_ARCH_SPARSEMEM_DEFAULT=y
-CONFIG_MAX_ACTIVE_REGIONS=1
-CONFIG_ARCH_POPULATES_NODE_MAP=y
-CONFIG_ARCH_SELECT_MEMORY_MODEL=y
-CONFIG_PAGE_SIZE_4KB=y
-# CONFIG_PAGE_SIZE_8KB is not set
-# CONFIG_PAGE_SIZE_16KB is not set
-# CONFIG_PAGE_SIZE_64KB is not set
-CONFIG_ENTRY_OFFSET=0x00001000
-CONFIG_SELECT_MEMORY_MODEL=y
-CONFIG_FLATMEM_MANUAL=y
-# CONFIG_DISCONTIGMEM_MANUAL is not set
-# CONFIG_SPARSEMEM_MANUAL is not set
-CONFIG_FLATMEM=y
-CONFIG_FLAT_NODE_MEM_MAP=y
-CONFIG_SPARSEMEM_STATIC=y
-CONFIG_PAGEFLAGS_EXTENDED=y
-CONFIG_SPLIT_PTLOCK_CPUS=4
-# CONFIG_RESOURCES_64BIT is not set
-# CONFIG_PHYS_ADDR_T_64BIT is not set
-CONFIG_ZONE_DMA_FLAG=0
-CONFIG_NR_QUICK=2
-CONFIG_UNEVICTABLE_LRU=y
-
-#
-# Cache configuration
-#
-# CONFIG_SH_DIRECT_MAPPED is not set
-CONFIG_CACHE_WRITEBACK=y
-# CONFIG_CACHE_WRITETHROUGH is not set
-# CONFIG_CACHE_OFF is not set
-
-#
-# Processor features
-#
-CONFIG_CPU_LITTLE_ENDIAN=y
-# CONFIG_CPU_BIG_ENDIAN is not set
-CONFIG_SH_FPU=y
-# CONFIG_SH_STORE_QUEUES is not set
-CONFIG_CPU_HAS_INTEVT=y
-CONFIG_CPU_HAS_SR_RB=y
-CONFIG_CPU_HAS_PTEA=y
-CONFIG_CPU_HAS_FPU=y
-
-#
-# Board support
-#
-# CONFIG_SH_7751_SYSTEMH is not set
-# CONFIG_SH_SECUREEDGE5410 is not set
-CONFIG_SH_RTS7751R2D=y
-# CONFIG_SH_LANDISK is not set
-# CONFIG_SH_TITAN is not set
-# CONFIG_SH_LBOX_RE2 is not set
-
-#
-# RTS7751R2D Board Revision
-#
-CONFIG_RTS7751R2D_PLUS=y
-# CONFIG_RTS7751R2D_1 is not set
-
-#
-# Timer and clock configuration
-#
-CONFIG_SH_TMU=y
-CONFIG_SH_TIMER_IRQ=16
-CONFIG_SH_PCLK_FREQ=60000000
-# CONFIG_NO_HZ is not set
-# CONFIG_HIGH_RES_TIMERS is not set
-CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
-
-#
-# CPU Frequency scaling
-#
-# CONFIG_CPU_FREQ is not set
-
-#
-# DMA support
-#
-# CONFIG_SH_DMA is not set
-
-#
-# Companion Chips
-#
-
-#
-# Additional SuperH Device Drivers
-#
-CONFIG_HEARTBEAT=y
-# CONFIG_PUSH_SWITCH is not set
-
-#
-# Kernel features
-#
-# CONFIG_HZ_100 is not set
-CONFIG_HZ_250=y
-# CONFIG_HZ_300 is not set
-# CONFIG_HZ_1000 is not set
-CONFIG_HZ=250
-# CONFIG_SCHED_HRTICK is not set
-# CONFIG_KEXEC is not set
-# CONFIG_CRASH_DUMP is not set
-CONFIG_SECCOMP=y
-CONFIG_PREEMPT_NONE=y
-# CONFIG_PREEMPT_VOLUNTARY is not set
-# CONFIG_PREEMPT is not set
-CONFIG_GUSA=y
-# CONFIG_GUSA_RB is not set
-
-#
-# Boot options
-#
-CONFIG_ZERO_PAGE_OFFSET=0x00010000
-CONFIG_BOOT_LINK_OFFSET=0x00800000
-# CONFIG_UBC_WAKEUP is not set
-CONFIG_CMDLINE_BOOL=y
-CONFIG_CMDLINE="console=tty0 console=ttySC0,115200 root=/dev/sda1 earlyprintk=serial"
-
-#
-# Bus options
-#
-# CONFIG_PCI is not set
-# CONFIG_ARCH_SUPPORTS_MSI is not set
-
-#
-# Executable file formats
-#
-CONFIG_BINFMT_ELF=y
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-# CONFIG_HAVE_AOUT is not set
-# CONFIG_BINFMT_MISC is not set
-# CONFIG_NET is not set
-
-#
-# Device Drivers
-#
-
-#
-# Generic Driver Options
-#
-CONFIG_STANDALONE=y
-CONFIG_PREVENT_FIRMWARE_BUILD=y
-# CONFIG_DEBUG_DRIVER is not set
-# CONFIG_DEBUG_DEVRES is not set
-# CONFIG_SYS_HYPERVISOR is not set
-# CONFIG_MTD is not set
-# CONFIG_PARPORT is not set
-CONFIG_BLK_DEV=y
-# CONFIG_BLK_DEV_COW_COMMON is not set
-# CONFIG_BLK_DEV_LOOP is not set
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_COUNT=16
-CONFIG_BLK_DEV_RAM_SIZE=4096
-# CONFIG_BLK_DEV_XIP is not set
-# CONFIG_CDROM_PKTCDVD is not set
-# CONFIG_BLK_DEV_HD is not set
-CONFIG_MISC_DEVICES=y
-# CONFIG_EEPROM_93CX6 is not set
-# CONFIG_ENCLOSURE_SERVICES is not set
-CONFIG_HAVE_IDE=y
-# CONFIG_IDE is not set
-
-#
-# SCSI device support
-#
-# CONFIG_RAID_ATTRS is not set
-CONFIG_SCSI=y
-CONFIG_SCSI_DMA=y
-# CONFIG_SCSI_TGT is not set
-# CONFIG_SCSI_NETLINK is not set
-CONFIG_SCSI_PROC_FS=y
-
-#
-# SCSI support type (disk, tape, CD-ROM)
-#
-CONFIG_BLK_DEV_SD=y
-# CONFIG_CHR_DEV_ST is not set
-# CONFIG_CHR_DEV_OSST is not set
-# CONFIG_BLK_DEV_SR is not set
-# CONFIG_CHR_DEV_SG is not set
-# CONFIG_CHR_DEV_SCH is not set
-
-#
-# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
-#
-# CONFIG_SCSI_MULTI_LUN is not set
-# CONFIG_SCSI_CONSTANTS is not set
-# CONFIG_SCSI_LOGGING is not set
-# CONFIG_SCSI_SCAN_ASYNC is not set
-CONFIG_SCSI_WAIT_SCAN=m
-
-#
-# SCSI Transports
-#
-# CONFIG_SCSI_SPI_ATTRS is not set
-# CONFIG_SCSI_FC_ATTRS is not set
-# CONFIG_SCSI_SAS_LIBSAS is not set
-# CONFIG_SCSI_SRP_ATTRS is not set
-CONFIG_SCSI_LOWLEVEL=y
-# CONFIG_SCSI_DEBUG is not set
-# CONFIG_SCSI_DH is not set
-CONFIG_ATA=y
-# CONFIG_ATA_NONSTANDARD is not set
-CONFIG_SATA_PMP=y
-CONFIG_ATA_SFF=y
-# CONFIG_SATA_MV is not set
-# CONFIG_PATA_PLATFORM is not set
-# CONFIG_MD is not set
-# CONFIG_PHONE is not set
-
-#
-# Input device support
-#
-CONFIG_INPUT=y
-# CONFIG_INPUT_FF_MEMLESS is not set
-# CONFIG_INPUT_POLLDEV is not set
-
-#
-# Userland interfaces
-#
-# CONFIG_INPUT_MOUSEDEV is not set
-# CONFIG_INPUT_JOYDEV is not set
-# CONFIG_INPUT_EVDEV is not set
-# CONFIG_INPUT_EVBUG is not set
-
-#
-# Input Device Drivers
-#
-# CONFIG_INPUT_KEYBOARD is not set
-# CONFIG_INPUT_MOUSE is not set
-# CONFIG_INPUT_JOYSTICK is not set
-# CONFIG_INPUT_TABLET is not set
-# CONFIG_INPUT_TOUCHSCREEN is not set
-# CONFIG_INPUT_MISC is not set
-
-#
-# Hardware I/O ports
-#
-# CONFIG_SERIO is not set
-# CONFIG_GAMEPORT is not set
-
-#
-# Character devices
-#
-CONFIG_VT=y
-CONFIG_CONSOLE_TRANSLATIONS=y
-CONFIG_VT_CONSOLE=y
-CONFIG_HW_CONSOLE=y
-CONFIG_VT_HW_CONSOLE_BINDING=y
-CONFIG_DEVKMEM=y
-# CONFIG_SERIAL_NONSTANDARD is not set
-
-#
-# Serial drivers
-#
-CONFIG_SERIAL_8250=y
-# CONFIG_SERIAL_8250_CONSOLE is not set
-CONFIG_SERIAL_8250_NR_UARTS=4
-CONFIG_SERIAL_8250_RUNTIME_UARTS=4
-# CONFIG_SERIAL_8250_EXTENDED is not set
-
-#
-# Non-8250 serial port support
-#
-CONFIG_SERIAL_SH_SCI=y
-CONFIG_SERIAL_SH_SCI_NR_UARTS=1
-CONFIG_SERIAL_SH_SCI_CONSOLE=y
-CONFIG_SERIAL_CORE=y
-CONFIG_SERIAL_CORE_CONSOLE=y
-CONFIG_UNIX98_PTYS=y
-CONFIG_LEGACY_PTYS=y
-CONFIG_LEGACY_PTY_COUNT=256
-# CONFIG_IPMI_HANDLER is not set
-CONFIG_HW_RANDOM=y
-# CONFIG_R3964 is not set
-# CONFIG_RAW_DRIVER is not set
-# CONFIG_TCG_TPM is not set
-# CONFIG_I2C is not set
-CONFIG_SPI=y
-# CONFIG_SPI_DEBUG is not set
-CONFIG_SPI_MASTER=y
-
-#
-# SPI Master Controller Drivers
-#
-CONFIG_SPI_BITBANG=y
-# CONFIG_SPI_SH_SCI is not set
-
-#
-# SPI Protocol Masters
-#
-# CONFIG_SPI_AT25 is not set
-# CONFIG_SPI_SPIDEV is not set
-# CONFIG_SPI_TLE62X0 is not set
-# CONFIG_W1 is not set
-# CONFIG_POWER_SUPPLY is not set
-CONFIG_HWMON=y
-# CONFIG_HWMON_VID is not set
-# CONFIG_SENSORS_ADCXX is not set
-# CONFIG_SENSORS_F71805F is not set
-# CONFIG_SENSORS_F71882FG is not set
-# CONFIG_SENSORS_IT87 is not set
-# CONFIG_SENSORS_LM70 is not set
-# CONFIG_SENSORS_MAX1111 is not set
-# CONFIG_SENSORS_PC87360 is not set
-# CONFIG_SENSORS_PC87427 is not set
-# CONFIG_SENSORS_SMSC47M1 is not set
-# CONFIG_SENSORS_SMSC47B397 is not set
-# CONFIG_SENSORS_VT1211 is not set
-# CONFIG_SENSORS_W83627HF is not set
-# CONFIG_SENSORS_W83627EHF is not set
-# CONFIG_HWMON_DEBUG_CHIP is not set
-# CONFIG_THERMAL is not set
-# CONFIG_THERMAL_HWMON is not set
-# CONFIG_WATCHDOG is not set
-
-#
-# Sonics Silicon Backplane
-#
-CONFIG_SSB_POSSIBLE=y
-# CONFIG_SSB is not set
-
-#
-# Multifunction device drivers
-#
-# CONFIG_MFD_CORE is not set
-CONFIG_MFD_SM501=y
-# CONFIG_HTC_PASIC3 is not set
-# CONFIG_MFD_TMIO is not set
-# CONFIG_MFD_WM8400 is not set
-
-#
-# Multimedia devices
-#
-
-#
-# Multimedia core support
-#
-# CONFIG_VIDEO_DEV is not set
-# CONFIG_VIDEO_MEDIA is not set
-
-#
-# Multimedia drivers
-#
-CONFIG_DAB=y
-
-#
-# Graphics support
-#
-# CONFIG_VGASTATE is not set
-CONFIG_VIDEO_OUTPUT_CONTROL=m
-CONFIG_FB=y
-# CONFIG_FIRMWARE_EDID is not set
-# CONFIG_FB_DDC is not set
-# CONFIG_FB_BOOT_VESA_SUPPORT is not set
-CONFIG_FB_CFB_FILLRECT=y
-CONFIG_FB_CFB_COPYAREA=y
-CONFIG_FB_CFB_IMAGEBLIT=y
-# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
-# CONFIG_FB_SYS_FILLRECT is not set
-# CONFIG_FB_SYS_COPYAREA is not set
-# CONFIG_FB_SYS_IMAGEBLIT is not set
-# CONFIG_FB_FOREIGN_ENDIAN is not set
-# CONFIG_FB_SYS_FOPS is not set
-# CONFIG_FB_SVGALIB is not set
-# CONFIG_FB_MACMODES is not set
-# CONFIG_FB_BACKLIGHT is not set
-# CONFIG_FB_MODE_HELPERS is not set
-# CONFIG_FB_TILEBLITTING is not set
-
-#
-# Frame buffer hardware drivers
-#
-# CONFIG_FB_S1D13XXX is not set
-CONFIG_FB_SH_MOBILE_LCDC=m
-CONFIG_FB_SM501=y
-# CONFIG_FB_VIRTUAL is not set
-# CONFIG_FB_METRONOME is not set
-# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
-
-#
-# Display device support
-#
-# CONFIG_DISPLAY_SUPPORT is not set
-
-#
-# Console display driver support
-#
-CONFIG_DUMMY_CONSOLE=y
-CONFIG_FRAMEBUFFER_CONSOLE=y
-# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
-# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
-# CONFIG_FONTS is not set
-CONFIG_FONT_8x8=y
-CONFIG_FONT_8x16=y
-CONFIG_LOGO=y
-# CONFIG_LOGO_LINUX_MONO is not set
-# CONFIG_LOGO_LINUX_VGA16 is not set
-# CONFIG_LOGO_LINUX_CLUT224 is not set
-# CONFIG_LOGO_SUPERH_MONO is not set
-# CONFIG_LOGO_SUPERH_VGA16 is not set
-CONFIG_LOGO_SUPERH_CLUT224=y
-CONFIG_SOUND=y
-CONFIG_SOUND_OSS_CORE=y
-CONFIG_SND=m
-# CONFIG_SND_SEQUENCER is not set
-# CONFIG_SND_MIXER_OSS is not set
-# CONFIG_SND_PCM_OSS is not set
-# CONFIG_SND_DYNAMIC_MINORS is not set
-CONFIG_SND_SUPPORT_OLD_API=y
-CONFIG_SND_VERBOSE_PROCFS=y
-# CONFIG_SND_VERBOSE_PRINTK is not set
-# CONFIG_SND_DEBUG is not set
-CONFIG_SND_DRIVERS=y
-# CONFIG_SND_DUMMY is not set
-# CONFIG_SND_MTPAV is not set
-# CONFIG_SND_SERIAL_U16550 is not set
-# CONFIG_SND_MPU401 is not set
-CONFIG_SND_SPI=y
-CONFIG_SND_SUPERH=y
-# CONFIG_SND_SOC is not set
-CONFIG_SOUND_PRIME=m
-CONFIG_HID_SUPPORT=y
-CONFIG_HID=y
-# CONFIG_HID_DEBUG is not set
-# CONFIG_HIDRAW is not set
-# CONFIG_HID_PID is not set
-
-#
-# Special HID drivers
-#
-CONFIG_HID_COMPAT=y
-# CONFIG_USB_SUPPORT is not set
-# CONFIG_MMC is not set
-# CONFIG_MEMSTICK is not set
-# CONFIG_NEW_LEDS is not set
-# CONFIG_ACCESSIBILITY is not set
-CONFIG_RTC_LIB=y
-CONFIG_RTC_CLASS=y
-CONFIG_RTC_HCTOSYS=y
-CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
-# CONFIG_RTC_DEBUG is not set
-
-#
-# RTC interfaces
-#
-CONFIG_RTC_INTF_SYSFS=y
-CONFIG_RTC_INTF_PROC=y
-CONFIG_RTC_INTF_DEV=y
-# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
-# CONFIG_RTC_DRV_TEST is not set
-
-#
-# SPI RTC drivers
-#
-# CONFIG_RTC_DRV_M41T94 is not set
-# CONFIG_RTC_DRV_DS1305 is not set
-# CONFIG_RTC_DRV_MAX6902 is not set
-CONFIG_RTC_DRV_R9701=y
-# CONFIG_RTC_DRV_RS5C348 is not set
-# CONFIG_RTC_DRV_DS3234 is not set
-
-#
-# Platform RTC drivers
-#
-# CONFIG_RTC_DRV_DS1286 is not set
-# CONFIG_RTC_DRV_DS1511 is not set
-# CONFIG_RTC_DRV_DS1553 is not set
-# CONFIG_RTC_DRV_DS1742 is not set
-# CONFIG_RTC_DRV_STK17TA8 is not set
-# CONFIG_RTC_DRV_M48T86 is not set
-# CONFIG_RTC_DRV_M48T35 is not set
-# CONFIG_RTC_DRV_M48T59 is not set
-# CONFIG_RTC_DRV_BQ4802 is not set
-# CONFIG_RTC_DRV_V3020 is not set
-
-#
-# on-CPU RTC drivers
-#
-# CONFIG_RTC_DRV_SH is not set
-# CONFIG_DMADEVICES is not set
-# CONFIG_UIO is not set
-# CONFIG_STAGING is not set
-
-#
-# File systems
-#
-CONFIG_EXT2_FS=y
-# CONFIG_EXT2_FS_XATTR is not set
-# CONFIG_EXT2_FS_XIP is not set
-# CONFIG_EXT3_FS is not set
-# CONFIG_EXT4_FS is not set
-# CONFIG_REISERFS_FS is not set
-# CONFIG_JFS_FS is not set
-# CONFIG_FS_POSIX_ACL is not set
-CONFIG_FILE_LOCKING=y
-# CONFIG_XFS_FS is not set
-CONFIG_DNOTIFY=y
-CONFIG_INOTIFY=y
-CONFIG_INOTIFY_USER=y
-# CONFIG_QUOTA is not set
-# CONFIG_AUTOFS_FS is not set
-# CONFIG_AUTOFS4_FS is not set
-# CONFIG_FUSE_FS is not set
-
-#
-# CD-ROM/DVD Filesystems
-#
-# CONFIG_ISO9660_FS is not set
-# CONFIG_UDF_FS is not set
-
-#
-# DOS/FAT/NT Filesystems
-#
-CONFIG_FAT_FS=y
-CONFIG_MSDOS_FS=y
-CONFIG_VFAT_FS=y
-CONFIG_FAT_DEFAULT_CODEPAGE=437
-CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
-# CONFIG_NTFS_FS is not set
-
-#
-# Pseudo filesystems
-#
-CONFIG_PROC_FS=y
-CONFIG_PROC_KCORE=y
-CONFIG_PROC_SYSCTL=y
-CONFIG_PROC_PAGE_MONITOR=y
-CONFIG_SYSFS=y
-CONFIG_TMPFS=y
-# CONFIG_TMPFS_POSIX_ACL is not set
-# CONFIG_HUGETLBFS is not set
-# CONFIG_HUGETLB_PAGE is not set
-# CONFIG_CONFIGFS_FS is not set
-
-#
-# Miscellaneous filesystems
-#
-# CONFIG_ADFS_FS is not set
-# CONFIG_AFFS_FS is not set
-# CONFIG_HFS_FS is not set
-# CONFIG_HFSPLUS_FS is not set
-# CONFIG_BEFS_FS is not set
-# CONFIG_BFS_FS is not set
-# CONFIG_EFS_FS is not set
-# CONFIG_CRAMFS is not set
-# CONFIG_VXFS_FS is not set
-CONFIG_MINIX_FS=y
-# CONFIG_OMFS_FS is not set
-# CONFIG_HPFS_FS is not set
-# CONFIG_QNX4FS_FS is not set
-# CONFIG_ROMFS_FS is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_UFS_FS is not set
-
-#
-# Partition Types
-#
-# CONFIG_PARTITION_ADVANCED is not set
-CONFIG_MSDOS_PARTITION=y
-CONFIG_NLS=y
-CONFIG_NLS_DEFAULT="iso8859-1"
-# CONFIG_NLS_CODEPAGE_437 is not set
-# CONFIG_NLS_CODEPAGE_737 is not set
-# CONFIG_NLS_CODEPAGE_775 is not set
-# CONFIG_NLS_CODEPAGE_850 is not set
-# CONFIG_NLS_CODEPAGE_852 is not set
-# CONFIG_NLS_CODEPAGE_855 is not set
-# CONFIG_NLS_CODEPAGE_857 is not set
-# CONFIG_NLS_CODEPAGE_860 is not set
-# CONFIG_NLS_CODEPAGE_861 is not set
-# CONFIG_NLS_CODEPAGE_862 is not set
-# CONFIG_NLS_CODEPAGE_863 is not set
-# CONFIG_NLS_CODEPAGE_864 is not set
-# CONFIG_NLS_CODEPAGE_865 is not set
-# CONFIG_NLS_CODEPAGE_866 is not set
-# CONFIG_NLS_CODEPAGE_869 is not set
-# CONFIG_NLS_CODEPAGE_936 is not set
-# CONFIG_NLS_CODEPAGE_950 is not set
-CONFIG_NLS_CODEPAGE_932=y
-# CONFIG_NLS_CODEPAGE_949 is not set
-# CONFIG_NLS_CODEPAGE_874 is not set
-# CONFIG_NLS_ISO8859_8 is not set
-# CONFIG_NLS_CODEPAGE_1250 is not set
-# CONFIG_NLS_CODEPAGE_1251 is not set
-# CONFIG_NLS_ASCII is not set
-# CONFIG_NLS_ISO8859_1 is not set
-# CONFIG_NLS_ISO8859_2 is not set
-# CONFIG_NLS_ISO8859_3 is not set
-# CONFIG_NLS_ISO8859_4 is not set
-# CONFIG_NLS_ISO8859_5 is not set
-# CONFIG_NLS_ISO8859_6 is not set
-# CONFIG_NLS_ISO8859_7 is not set
-# CONFIG_NLS_ISO8859_9 is not set
-# CONFIG_NLS_ISO8859_13 is not set
-# CONFIG_NLS_ISO8859_14 is not set
-# CONFIG_NLS_ISO8859_15 is not set
-# CONFIG_NLS_KOI8_R is not set
-# CONFIG_NLS_KOI8_U is not set
-# CONFIG_NLS_UTF8 is not set
-
-#
-# Kernel hacking
-#
-CONFIG_TRACE_IRQFLAGS_SUPPORT=y
-# CONFIG_PRINTK_TIME is not set
-CONFIG_ENABLE_WARN_DEPRECATED=y
-CONFIG_ENABLE_MUST_CHECK=y
-CONFIG_FRAME_WARN=1024
-# CONFIG_MAGIC_SYSRQ is not set
-# CONFIG_UNUSED_SYMBOLS is not set
-CONFIG_DEBUG_FS=y
-# CONFIG_HEADERS_CHECK is not set
-CONFIG_DEBUG_KERNEL=y
-# CONFIG_DEBUG_SHIRQ is not set
-CONFIG_DETECT_SOFTLOCKUP=y
-# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
-CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
-CONFIG_SCHED_DEBUG=y
-# CONFIG_SCHEDSTATS is not set
-# CONFIG_TIMER_STATS is not set
-# CONFIG_DEBUG_OBJECTS is not set
-# CONFIG_DEBUG_SLAB is not set
-# CONFIG_DEBUG_RT_MUTEXES is not set
-# CONFIG_RT_MUTEX_TESTER is not set
-# CONFIG_DEBUG_SPINLOCK is not set
-# CONFIG_DEBUG_MUTEXES is not set
-# CONFIG_DEBUG_LOCK_ALLOC is not set
-# CONFIG_PROVE_LOCKING is not set
-# CONFIG_LOCK_STAT is not set
-# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
-# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
-# CONFIG_DEBUG_KOBJECT is not set
-# CONFIG_DEBUG_BUGVERBOSE is not set
-CONFIG_DEBUG_INFO=y
-# CONFIG_DEBUG_VM is not set
-# CONFIG_DEBUG_WRITECOUNT is not set
-# CONFIG_DEBUG_MEMORY_INIT is not set
-# CONFIG_DEBUG_LIST is not set
-# CONFIG_DEBUG_SG is not set
-# CONFIG_FRAME_POINTER is not set
-# CONFIG_RCU_TORTURE_TEST is not set
-# CONFIG_RCU_CPU_STALL_DETECTOR is not set
-# CONFIG_BACKTRACE_SELF_TEST is not set
-# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
-# CONFIG_FAULT_INJECTION is not set
-# CONFIG_LATENCYTOP is not set
-CONFIG_NOP_TRACER=y
-CONFIG_HAVE_FTRACE=y
-# CONFIG_FTRACE is not set
-# CONFIG_IRQSOFF_TRACER is not set
-# CONFIG_SCHED_TRACER is not set
-# CONFIG_CONTEXT_SWITCH_TRACER is not set
-# CONFIG_BOOT_TRACER is not set
-# CONFIG_STACK_TRACER is not set
-# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
-# CONFIG_SAMPLES is not set
-# CONFIG_SH_STANDARD_BIOS is not set
-CONFIG_EARLY_SCIF_CONSOLE=y
-CONFIG_EARLY_SCIF_CONSOLE_PORT=0xffe80000
-CONFIG_EARLY_PRINTK=y
-# CONFIG_DEBUG_BOOTMEM is not set
-# CONFIG_DEBUG_STACKOVERFLOW is not set
-# CONFIG_DEBUG_STACK_USAGE is not set
-# CONFIG_4KSTACKS is not set
-# CONFIG_IRQSTACKS is not set
-# CONFIG_SH_KGDB is not set
-
-#
-# Security options
-#
-# CONFIG_KEYS is not set
-# CONFIG_SECURITY is not set
-# CONFIG_SECURITYFS is not set
-# CONFIG_SECURITY_FILE_CAPABILITIES is not set
-CONFIG_CRYPTO=y
-
-#
-# Crypto core or helper
-#
-# CONFIG_CRYPTO_FIPS is not set
-# CONFIG_CRYPTO_MANAGER is not set
-# CONFIG_CRYPTO_GF128MUL is not set
-# CONFIG_CRYPTO_NULL is not set
-# CONFIG_CRYPTO_CRYPTD is not set
-# CONFIG_CRYPTO_AUTHENC is not set
-# CONFIG_CRYPTO_TEST is not set
-
-#
-# Authenticated Encryption with Associated Data
-#
-# CONFIG_CRYPTO_CCM is not set
-# CONFIG_CRYPTO_GCM is not set
-# CONFIG_CRYPTO_SEQIV is not set
-
-#
-# Block modes
-#
-# CONFIG_CRYPTO_CBC is not set
-# CONFIG_CRYPTO_CTR is not set
-# CONFIG_CRYPTO_CTS is not set
-# CONFIG_CRYPTO_ECB is not set
-# CONFIG_CRYPTO_LRW is not set
-# CONFIG_CRYPTO_PCBC is not set
-# CONFIG_CRYPTO_XTS is not set
-
-#
-# Hash modes
-#
-# CONFIG_CRYPTO_HMAC is not set
-# CONFIG_CRYPTO_XCBC is not set
-
-#
-# Digest
-#
-# CONFIG_CRYPTO_CRC32C is not set
-# CONFIG_CRYPTO_MD4 is not set
-# CONFIG_CRYPTO_MD5 is not set
-# CONFIG_CRYPTO_MICHAEL_MIC is not set
-# CONFIG_CRYPTO_RMD128 is not set
-# CONFIG_CRYPTO_RMD160 is not set
-# CONFIG_CRYPTO_RMD256 is not set
-# CONFIG_CRYPTO_RMD320 is not set
-# CONFIG_CRYPTO_SHA1 is not set
-# CONFIG_CRYPTO_SHA256 is not set
-# CONFIG_CRYPTO_SHA512 is not set
-# CONFIG_CRYPTO_TGR192 is not set
-# CONFIG_CRYPTO_WP512 is not set
-
-#
-# Ciphers
-#
-# CONFIG_CRYPTO_AES is not set
-# CONFIG_CRYPTO_ANUBIS is not set
-# CONFIG_CRYPTO_ARC4 is not set
-# CONFIG_CRYPTO_BLOWFISH is not set
-# CONFIG_CRYPTO_CAMELLIA is not set
-# CONFIG_CRYPTO_CAST5 is not set
-# CONFIG_CRYPTO_CAST6 is not set
-# CONFIG_CRYPTO_DES is not set
-# CONFIG_CRYPTO_FCRYPT is not set
-# CONFIG_CRYPTO_KHAZAD is not set
-# CONFIG_CRYPTO_SALSA20 is not set
-# CONFIG_CRYPTO_SEED is not set
-# CONFIG_CRYPTO_SERPENT is not set
-# CONFIG_CRYPTO_TEA is not set
-# CONFIG_CRYPTO_TWOFISH is not set
-
-#
-# Compression
-#
-# CONFIG_CRYPTO_DEFLATE is not set
-# CONFIG_CRYPTO_LZO is not set
-
-#
-# Random Number Generation
-#
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
-CONFIG_CRYPTO_HW=y
-
-#
-# Library routines
-#
-CONFIG_BITREVERSE=y
-# CONFIG_CRC_CCITT is not set
-# CONFIG_CRC16 is not set
-CONFIG_CRC_T10DIF=y
-# CONFIG_CRC_ITU_T is not set
-CONFIG_CRC32=y
-# CONFIG_CRC7 is not set
-# CONFIG_LIBCRC32C is not set
-CONFIG_PLIST=y
-CONFIG_HAS_IOMEM=y
-CONFIG_HAS_IOPORT=y
-CONFIG_HAS_DMA=y
index 075f42e..be246f3 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.27
-# Wed Oct 22 19:00:21 2008
+# Linux kernel version: 2.6.28-rc6
+# Thu Dec  4 16:40:25 2008
 #
 CONFIG_SUPERH=y
 CONFIG_SUPERH32=y
@@ -74,7 +74,6 @@ CONFIG_EVENTFD=y
 # CONFIG_SHMEM is not set
 CONFIG_AIO=y
 CONFIG_VM_EVENT_COUNTERS=y
-CONFIG_PCI_QUIRKS=y
 CONFIG_SLAB=y
 # CONFIG_SLUB is not set
 # CONFIG_SLOB is not set
@@ -127,6 +126,7 @@ CONFIG_CPU_SH4=y
 CONFIG_CPU_SH4A=y
 CONFIG_CPU_SH4AL_DSP=y
 # CONFIG_CPU_SUBTYPE_SH7619 is not set
+# CONFIG_CPU_SUBTYPE_SH7201 is not set
 # CONFIG_CPU_SUBTYPE_SH7203 is not set
 # CONFIG_CPU_SUBTYPE_SH7206 is not set
 # CONFIG_CPU_SUBTYPE_SH7263 is not set
@@ -227,7 +227,7 @@ CONFIG_SH_7343_SOLUTION_ENGINE=y
 #
 CONFIG_SH_TMU=y
 CONFIG_SH_TIMER_IRQ=16
-CONFIG_SH_PCLK_FREQ=27000000
+CONFIG_SH_PCLK_FREQ=33333333
 # CONFIG_NO_HZ is not set
 # CONFIG_HIGH_RES_TIMERS is not set
 CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
@@ -274,7 +274,8 @@ CONFIG_GUSA=y
 #
 CONFIG_ZERO_PAGE_OFFSET=0x00001000
 CONFIG_BOOT_LINK_OFFSET=0x00800000
-# CONFIG_CMDLINE_BOOL is not set
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttySC0,115200"
 
 #
 # Bus options
@@ -463,6 +464,7 @@ CONFIG_BLK_DEV=y
 # CONFIG_BLK_DEV_COW_COMMON is not set
 # CONFIG_BLK_DEV_LOOP is not set
 # CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_UB is not set
 # CONFIG_BLK_DEV_RAM is not set
 # CONFIG_CDROM_PKTCDVD is not set
 # CONFIG_ATA_OVER_ETH is not set
@@ -519,23 +521,10 @@ CONFIG_NETDEVICES=y
 # CONFIG_EQUALIZER is not set
 # CONFIG_TUN is not set
 # CONFIG_VETH is not set
-# CONFIG_PHYLIB is not set
-CONFIG_NET_ETHERNET=y
+# CONFIG_NET_ETHERNET is not set
 CONFIG_MII=y
-# CONFIG_AX88796 is not set
-# CONFIG_STNIC is not set
-CONFIG_SMC91X=y
-# CONFIG_SMC911X is not set
-# CONFIG_IBM_NEW_EMAC_ZMII is not set
-# CONFIG_IBM_NEW_EMAC_RGMII is not set
-# CONFIG_IBM_NEW_EMAC_TAH is not set
-# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
-# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
-# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
-# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
-# CONFIG_B44 is not set
-CONFIG_NETDEV_1000=y
-CONFIG_NETDEV_10000=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
 
 #
 # Wireless LAN
@@ -543,6 +532,26 @@ CONFIG_NETDEV_10000=y
 # CONFIG_WLAN_PRE80211 is not set
 # CONFIG_WLAN_80211 is not set
 # CONFIG_IWLWIFI_LEDS is not set
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+CONFIG_USB_USBNET=y
+# CONFIG_USB_NET_AX8817X is not set
+CONFIG_USB_NET_CDCETHER=y
+CONFIG_USB_NET_DM9601=y
+# CONFIG_USB_NET_SMSC95XX is not set
+# CONFIG_USB_NET_GL620A is not set
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_MCS7830 is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_ZAURUS is not set
 # CONFIG_WAN is not set
 # CONFIG_PPP is not set
 # CONFIG_SLIP is not set
@@ -597,13 +606,17 @@ CONFIG_DEVKMEM=y
 #
 # Serial drivers
 #
-# CONFIG_SERIAL_8250 is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_CONSOLE is not set
+CONFIG_SERIAL_8250_NR_UARTS=2
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+# CONFIG_SERIAL_8250_EXTENDED is not set
 
 #
 # Non-8250 serial port support
 #
 CONFIG_SERIAL_SH_SCI=y
-CONFIG_SERIAL_SH_SCI_NR_UARTS=2
+CONFIG_SERIAL_SH_SCI_NR_UARTS=4
 CONFIG_SERIAL_SH_SCI_CONSOLE=y
 CONFIG_SERIAL_CORE=y
 CONFIG_SERIAL_CORE_CONSOLE=y
@@ -615,7 +628,51 @@ CONFIG_HW_RANDOM=y
 # CONFIG_R3964 is not set
 # CONFIG_RAW_DRIVER is not set
 # CONFIG_TCG_TPM is not set
-# CONFIG_I2C is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_CHARDEV is not set
+CONFIG_I2C_HELPER_AUTO=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_OCORES is not set
+CONFIG_I2C_SH_MOBILE=y
+# CONFIG_I2C_SIMTEC is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_STUB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_AT24 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
 # CONFIG_SPI is not set
 # CONFIG_W1 is not set
 # CONFIG_POWER_SUPPLY is not set
@@ -623,11 +680,11 @@ CONFIG_HW_RANDOM=y
 # CONFIG_THERMAL is not set
 # CONFIG_THERMAL_HWMON is not set
 # CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
 
 #
 # Sonics Silicon Backplane
 #
-CONFIG_SSB_POSSIBLE=y
 # CONFIG_SSB is not set
 
 #
@@ -637,7 +694,10 @@ CONFIG_SSB_POSSIBLE=y
 # CONFIG_MFD_SM501 is not set
 # CONFIG_HTC_PASIC3 is not set
 # CONFIG_MFD_TMIO is not set
+# CONFIG_PMIC_DA903X is not set
 # CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_REGULATOR is not set
 
 #
 # Multimedia devices
@@ -657,6 +717,16 @@ CONFIG_VIDEO_MEDIA=y
 # Multimedia drivers
 #
 # CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=y
+# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set
+CONFIG_MEDIA_TUNER_SIMPLE=y
+CONFIG_MEDIA_TUNER_TDA8290=y
+CONFIG_MEDIA_TUNER_TDA9887=y
+CONFIG_MEDIA_TUNER_TEA5761=y
+CONFIG_MEDIA_TUNER_TEA5767=y
+CONFIG_MEDIA_TUNER_MT20XX=y
+CONFIG_MEDIA_TUNER_XC2028=y
+CONFIG_MEDIA_TUNER_XC5000=y
 CONFIG_VIDEO_V4L2=y
 CONFIG_VIDEO_V4L1=y
 CONFIG_VIDEO_CAPTURE_DRIVERS=y
@@ -665,8 +735,57 @@ CONFIG_VIDEO_CAPTURE_DRIVERS=y
 CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
 # CONFIG_VIDEO_VIVI is not set
 # CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
 # CONFIG_SOC_CAMERA is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_USB_VIDEO_CLASS is not set
+CONFIG_USB_GSPCA=m
+# CONFIG_USB_M5602 is not set
+# CONFIG_USB_GSPCA_CONEX is not set
+# CONFIG_USB_GSPCA_ETOMS is not set
+# CONFIG_USB_GSPCA_FINEPIX is not set
+# CONFIG_USB_GSPCA_MARS is not set
+# CONFIG_USB_GSPCA_OV519 is not set
+# CONFIG_USB_GSPCA_PAC207 is not set
+# CONFIG_USB_GSPCA_PAC7311 is not set
+# CONFIG_USB_GSPCA_SONIXB is not set
+# CONFIG_USB_GSPCA_SONIXJ is not set
+# CONFIG_USB_GSPCA_SPCA500 is not set
+# CONFIG_USB_GSPCA_SPCA501 is not set
+# CONFIG_USB_GSPCA_SPCA505 is not set
+# CONFIG_USB_GSPCA_SPCA506 is not set
+# CONFIG_USB_GSPCA_SPCA508 is not set
+# CONFIG_USB_GSPCA_SPCA561 is not set
+# CONFIG_USB_GSPCA_STK014 is not set
+# CONFIG_USB_GSPCA_SUNPLUS is not set
+# CONFIG_USB_GSPCA_T613 is not set
+# CONFIG_USB_GSPCA_TV8532 is not set
+# CONFIG_USB_GSPCA_VC032X is not set
+# CONFIG_USB_GSPCA_ZC3XX is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_USBVISION is not set
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_VIDEO_OVCAMCHIP is not set
+# CONFIG_USB_OV511 is not set
+# CONFIG_USB_SE401 is not set
+# CONFIG_USB_SN9C102 is not set
+# CONFIG_USB_STV680 is not set
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+# CONFIG_USB_STKWEBCAM is not set
+# CONFIG_USB_S2255 is not set
 CONFIG_RADIO_ADAPTERS=y
+# CONFIG_USB_DSBR is not set
+# CONFIG_USB_SI470X is not set
+# CONFIG_USB_MR800 is not set
 # CONFIG_DAB is not set
 
 #
@@ -700,6 +819,7 @@ CONFIG_FB_CFB_IMAGEBLIT=m
 CONFIG_FB_SH_MOBILE_LCDC=m
 # CONFIG_FB_VIRTUAL is not set
 # CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
 # CONFIG_BACKLIGHT_LCD_SUPPORT is not set
 
 #
@@ -737,27 +857,147 @@ CONFIG_SND_DRIVERS=y
 # CONFIG_SND_SERIAL_U16550 is not set
 # CONFIG_SND_MPU401 is not set
 CONFIG_SND_SUPERH=y
+CONFIG_SND_USB=y
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
 # CONFIG_SND_SOC is not set
 # CONFIG_SOUND_PRIME is not set
 CONFIG_HID_SUPPORT=y
 CONFIG_HID=y
 # CONFIG_HID_DEBUG is not set
 # CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=y
 # CONFIG_HID_PID is not set
+# CONFIG_USB_HIDDEV is not set
 
 #
 # Special HID drivers
 #
 CONFIG_HID_COMPAT=y
-# CONFIG_USB_SUPPORT is not set
+CONFIG_HID_A4TECH=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_BRIGHT=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DELL=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_LOGITECH=y
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_PANTHERLORD=y
+# CONFIG_PANTHERLORD_FF is not set
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SUNPLUS=y
+# CONFIG_THRUSTMASTER_FF is not set
+# CONFIG_ZEROPLUS_FF is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=y
+CONFIG_USB_DEBUG=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+# CONFIG_USB_MON is not set
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+CONFIG_USB_ISP116X_HCD=y
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_TMC is not set
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed;
+#
+
+#
+# see USB_STORAGE Help for more information
+#
+# CONFIG_USB_STORAGE is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_BERRY_CHARGE is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_VST is not set
+# CONFIG_USB_GADGET is not set
 # CONFIG_MMC is not set
 # CONFIG_MEMSTICK is not set
 # CONFIG_NEW_LEDS is not set
 # CONFIG_ACCESSIBILITY is not set
 # CONFIG_RTC_CLASS is not set
 # CONFIG_DMADEVICES is not set
-# CONFIG_UIO is not set
+CONFIG_UIO=y
+# CONFIG_UIO_PDRV is not set
+# CONFIG_UIO_PDRV_GENIRQ is not set
+# CONFIG_UIO_SMX is not set
+# CONFIG_UIO_SERCOS3 is not set
 # CONFIG_STAGING is not set
+CONFIG_STAGING_EXCLUDE_BUILD=y
 
 #
 # File systems
@@ -889,8 +1129,13 @@ CONFIG_FRAME_WARN=1024
 # CONFIG_DEBUG_MEMORY_INIT is not set
 # CONFIG_RCU_CPU_STALL_DETECTOR is not set
 # CONFIG_LATENCYTOP is not set
-CONFIG_NOP_TRACER=y
-CONFIG_HAVE_FTRACE=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+
+#
+# Tracers
+#
 # CONFIG_DYNAMIC_PRINTK_DEBUG is not set
 # CONFIG_SAMPLES is not set
 # CONFIG_SH_STANDARD_BIOS is not set
index 1ac812d..ab956ad 100644 (file)
@@ -3,7 +3,6 @@
 #
 
 obj-$(CONFIG_SH_DMA_API)       += dma-api.o dma-sysfs.o
-obj-$(CONFIG_ISA_DMA_API)      += dma-isa.o
 obj-$(CONFIG_SH_DMA)           += dma-sh.o
 obj-$(CONFIG_SH_DREAMCAST)     += dma-pvr2.o dma-g2.o
 obj-$(CONFIG_SH_DMABRG)                += dmabrg.o
diff --git a/arch/sh/drivers/dma/dma-isa.c b/arch/sh/drivers/dma/dma-isa.c
deleted file mode 100644 (file)
index 5fb044b..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * arch/sh/drivers/dma/dma-isa.c
- *
- * Generic ISA DMA wrapper for SH DMA API
- *
- * Copyright (C) 2003, 2004  Paul Mundt
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <asm/dma.h>
-
-/*
- * This implements a small wrapper set to make code using the old ISA DMA API
- * work with the SH DMA API. Since most of the work in the new API happens
- * at ops->xfer() time, we simply use the various set_dma_xxx() routines to
- * fill in per-channel info, and then hand hand this off to ops->xfer() at
- * enable_dma() time.
- *
- * For channels that are doing on-demand data transfer via cascading, the
- * channel itself will still need to be configured through the new API. As
- * such, this code is meant for only the simplest of tasks (and shouldn't be
- * used in any new drivers at all).
- *
- * NOTE: ops->xfer() is the preferred way of doing things. However, there
- * are some users of the ISA DMA API that exist in common code that we
- * don't necessarily want to go out of our way to break, so we still
- * allow for some compatibility at that level. Any new code is strongly
- * advised to run far away from the ISA DMA API and use the SH DMA API
- * directly.
- */
-unsigned long claim_dma_lock(void)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&dma_spin_lock, flags);
-
-       return flags;
-}
-EXPORT_SYMBOL(claim_dma_lock);
-
-void release_dma_lock(unsigned long flags)
-{
-       spin_unlock_irqrestore(&dma_spin_lock, flags);
-}
-EXPORT_SYMBOL(release_dma_lock);
-
-void disable_dma(unsigned int chan)
-{
-       /* Nothing */
-}
-EXPORT_SYMBOL(disable_dma);
-
-void enable_dma(unsigned int chan)
-{
-       struct dma_info *info = get_dma_info(chan);
-       struct dma_channel *channel = &info->channels[chan];
-
-       info->ops->xfer(channel);
-}
-EXPORT_SYMBOL(enable_dma);
-
-void clear_dma_ff(unsigned int chan)
-{
-       /* Nothing */
-}
-EXPORT_SYMBOL(clear_dma_ff);
-
-void set_dma_mode(unsigned int chan, char mode)
-{
-       struct dma_info *info = get_dma_info(chan);
-       struct dma_channel *channel = &info->channels[chan];
-
-       channel->mode = mode;
-}
-EXPORT_SYMBOL(set_dma_mode);
-
-void set_dma_addr(unsigned int chan, unsigned int addr)
-{
-       struct dma_info *info = get_dma_info(chan);
-       struct dma_channel *channel = &info->channels[chan];
-
-       /*
-        * Single address mode is the only thing supported through
-        * this interface.
-        */
-       if ((channel->mode & DMA_MODE_MASK) == DMA_MODE_READ) {
-               channel->sar = addr;
-       } else {
-               channel->dar = addr;
-       }
-}
-EXPORT_SYMBOL(set_dma_addr);
-
-void set_dma_count(unsigned int chan, unsigned int count)
-{
-       struct dma_info *info = get_dma_info(chan);
-       struct dma_channel *channel = &info->channels[chan];
-
-       channel->count = count;
-}
-EXPORT_SYMBOL(set_dma_count);
-
index b2ffe64..50887a5 100644 (file)
@@ -205,7 +205,8 @@ static int sh_dmac_get_dma_residue(struct dma_channel *chan)
 
 #if defined(CONFIG_CPU_SUBTYPE_SH7720) || \
     defined(CONFIG_CPU_SUBTYPE_SH7721) || \
-    defined(CONFIG_CPU_SUBTYPE_SH7780)
+    defined(CONFIG_CPU_SUBTYPE_SH7780) || \
+    defined(CONFIG_CPU_SUBTYPE_SH7709)
 #define dmaor_read_reg()       ctrl_inw(DMAOR)
 #define dmaor_write_reg(data)  ctrl_outw(data, DMAOR)
 #else
index b05af34..05fecd5 100644 (file)
@@ -29,6 +29,7 @@
 #define RS_IN  0x00000200
 #define RS_OUT 0x00000300
 #define TS_BLK 0x00000040
+#define TM_BUR 0x00000020
 #define CHCR_DE 0x00000001
 #define CHCR_TE 0x00000002
 #define CHCR_IE 0x00000004
index ebb58e6..e1703ff 100644 (file)
@@ -18,7 +18,8 @@
  */
 int __init pcibios_init_platform(void)
 {
-   return 1;
+       __set_io_port_base(SH7751_PCI_IO_BASE);
+       return 1;
 }
 
 static struct resource sh7751_io_resource = {
index b2a2bfa..078dc44 100644 (file)
@@ -123,16 +123,14 @@ int __init sh7780_pcic_init(struct sh4_pci_address_map *map)
         * Window0 = map->window0.size @ non-cached area base = SDRAM
         * Window1 = map->window1.size @ cached area base = SDRAM
         */
-       word = ((map->window0.size - 1) & 0x1ff00001) | 0x01;
-       pci_write_reg(0x07f00001, SH4_PCILSR0);
-       word = ((map->window1.size - 1) & 0x1ff00001) | 0x01;
+       word = (CONFIG_MEMORY_SIZE - 0x00100000) | 0x00000001;
+       pci_write_reg(word, SH4_PCILSR0);
        pci_write_reg(0x00000001, SH4_PCILSR1);
        /* Set the values on window 0 PCI config registers */
-       word = P2SEGADDR(map->window0.base);
-       pci_write_reg(0xa8000000, SH4_PCILAR0);
-       pci_write_reg(0x08000000, SH7780_PCIMBAR0);
+       word = (CONFIG_MEMORY_SIZE > 0x08000000) ? 0x10000000 : 0x08000000;
+       pci_write_reg(word | 0xa0000000, SH4_PCILAR0);
+       pci_write_reg(word, SH7780_PCIMBAR0);
        /* Set the values on window 1 PCI config registers */
-       word = P2SEGADDR(map->window1.base);
        pci_write_reg(0x00000000, SH4_PCILAR1);
        pci_write_reg(0x00000000, SH7780_PCIMBAR1);
 
index 2702d81..36736c7 100644 (file)
 /* Check if an address can be reached in 29 bits */
 #define IS_29BIT(a)    (((unsigned long)(a)) < 0x20000000)
 
+#ifdef CONFIG_SH_STORE_QUEUES
+/*
+ * This is a special case for the SH-4 store queues, as pages for this
+ * space still need to be faulted in before it's possible to flush the
+ * store queue cache for writeout to the remapped region.
+ */
+#define P3_ADDR_MAX            (P4SEG_STORE_QUE + 0x04000000)
+#else
+#define P3_ADDR_MAX            P4SEG
+#endif
+
 #endif /* __KERNEL__ */
 #endif /* __ASM_SH_ADDRSPACE_H */
index a5907b9..e73af33 100644 (file)
@@ -166,4 +166,7 @@ static inline int test_and_change_bit(int nr, volatile void * addr)
 
         return retval;
 }
+
+#include <asm-generic/bitops/non-atomic.h>
+
 #endif /* __ASM_SH_BITOPS_GRB_H */
diff --git a/arch/sh/include/asm/bitops-irq.h b/arch/sh/include/asm/bitops-irq.h
deleted file mode 100644 (file)
index 653a127..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-#ifndef __ASM_SH_BITOPS_IRQ_H
-#define __ASM_SH_BITOPS_IRQ_H
-
-static inline void set_bit(int nr, volatile void *addr)
-{
-       int     mask;
-       volatile unsigned int *a = addr;
-       unsigned long flags;
-
-       a += nr >> 5;
-       mask = 1 << (nr & 0x1f);
-       local_irq_save(flags);
-       *a |= mask;
-       local_irq_restore(flags);
-}
-
-static inline void clear_bit(int nr, volatile void *addr)
-{
-       int     mask;
-       volatile unsigned int *a = addr;
-       unsigned long flags;
-
-       a += nr >> 5;
-       mask = 1 << (nr & 0x1f);
-       local_irq_save(flags);
-       *a &= ~mask;
-       local_irq_restore(flags);
-}
-
-static inline void change_bit(int nr, volatile void *addr)
-{
-       int     mask;
-       volatile unsigned int *a = addr;
-       unsigned long flags;
-
-       a += nr >> 5;
-       mask = 1 << (nr & 0x1f);
-       local_irq_save(flags);
-       *a ^= mask;
-       local_irq_restore(flags);
-}
-
-static inline int test_and_set_bit(int nr, volatile void *addr)
-{
-       int     mask, retval;
-       volatile unsigned int *a = addr;
-       unsigned long flags;
-
-       a += nr >> 5;
-       mask = 1 << (nr & 0x1f);
-       local_irq_save(flags);
-       retval = (mask & *a) != 0;
-       *a |= mask;
-       local_irq_restore(flags);
-
-       return retval;
-}
-
-static inline int test_and_clear_bit(int nr, volatile void *addr)
-{
-       int     mask, retval;
-       volatile unsigned int *a = addr;
-       unsigned long flags;
-
-       a += nr >> 5;
-       mask = 1 << (nr & 0x1f);
-       local_irq_save(flags);
-       retval = (mask & *a) != 0;
-       *a &= ~mask;
-       local_irq_restore(flags);
-
-       return retval;
-}
-
-static inline int test_and_change_bit(int nr, volatile void *addr)
-{
-       int     mask, retval;
-       volatile unsigned int *a = addr;
-       unsigned long flags;
-
-       a += nr >> 5;
-       mask = 1 << (nr & 0x1f);
-       local_irq_save(flags);
-       retval = (mask & *a) != 0;
-       *a ^= mask;
-       local_irq_restore(flags);
-
-       return retval;
-}
-
-#endif /* __ASM_SH_BITOPS_IRQ_H */
index 43b8e1a..1d2fc0b 100644 (file)
@@ -141,4 +141,6 @@ static inline int test_and_change_bit(int nr, volatile void * addr)
        return retval != 0;
 }
 
+#include <asm-generic/bitops/non-atomic.h>
+
 #endif /* __ASM_SH_BITOPS_LLSC_H */
diff --git a/arch/sh/include/asm/bitops-op32.h b/arch/sh/include/asm/bitops-op32.h
new file mode 100644 (file)
index 0000000..f0ae7e9
--- /dev/null
@@ -0,0 +1,142 @@
+#ifndef __ASM_SH_BITOPS_OP32_H
+#define __ASM_SH_BITOPS_OP32_H
+
+/*
+ * The bit modifying instructions on SH-2A are only capable of working
+ * with a 3-bit immediate, which signifies the shift position for the bit
+ * being worked on.
+ */
+#if defined(__BIG_ENDIAN)
+#define BITOP_LE_SWIZZLE       ((BITS_PER_LONG-1) & ~0x7)
+#define BYTE_NUMBER(nr)                ((nr ^ BITOP_LE_SWIZZLE) / BITS_PER_BYTE)
+#define BYTE_OFFSET(nr)                ((nr ^ BITOP_LE_SWIZZLE) % BITS_PER_BYTE)
+#else
+#define BYTE_NUMBER(nr)                ((nr) / BITS_PER_BYTE)
+#define BYTE_OFFSET(nr)                ((nr) % BITS_PER_BYTE)
+#endif
+
+#define IS_IMMEDIATE(nr)       (__builtin_constant_p(nr))
+
+static inline void __set_bit(int nr, volatile unsigned long *addr)
+{
+       if (IS_IMMEDIATE(nr)) {
+               __asm__ __volatile__ (
+                       "bset.b %1, @(%O2,%0)           ! __set_bit\n\t"
+                       : "+r" (addr)
+                       : "i" (BYTE_OFFSET(nr)), "i" (BYTE_NUMBER(nr))
+                       : "t", "memory"
+               );
+       } else {
+               unsigned long mask = BIT_MASK(nr);
+               unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+
+               *p |= mask;
+       }
+}
+
+static inline void __clear_bit(int nr, volatile unsigned long *addr)
+{
+       if (IS_IMMEDIATE(nr)) {
+               __asm__ __volatile__ (
+                       "bclr.b %1, @(%O2,%0)           ! __clear_bit\n\t"
+                       : "+r" (addr)
+                       : "i" (BYTE_OFFSET(nr)),
+                         "i" (BYTE_NUMBER(nr))
+                       : "t", "memory"
+               );
+       } else {
+               unsigned long mask = BIT_MASK(nr);
+               unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+
+               *p &= ~mask;
+       }
+}
+
+/**
+ * __change_bit - Toggle a bit in memory
+ * @nr: the bit to change
+ * @addr: the address to start counting from
+ *
+ * Unlike change_bit(), this function is non-atomic and may be reordered.
+ * If it's called on the same region of memory simultaneously, the effect
+ * may be that only one operation succeeds.
+ */
+static inline void __change_bit(int nr, volatile unsigned long *addr)
+{
+       if (IS_IMMEDIATE(nr)) {
+               __asm__ __volatile__ (
+                       "bxor.b %1, @(%O2,%0)           ! __change_bit\n\t"
+                       : "+r" (addr)
+                       : "i" (BYTE_OFFSET(nr)),
+                         "i" (BYTE_NUMBER(nr))
+                       : "t", "memory"
+               );
+       } else {
+               unsigned long mask = BIT_MASK(nr);
+               unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+
+               *p ^= mask;
+       }
+}
+
+/**
+ * __test_and_set_bit - Set a bit and return its old value
+ * @nr: Bit to set
+ * @addr: Address to count from
+ *
+ * This operation is non-atomic and can be reordered.
+ * If two examples of this operation race, one can appear to succeed
+ * but actually fail.  You must protect multiple accesses with a lock.
+ */
+static inline int __test_and_set_bit(int nr, volatile unsigned long *addr)
+{
+       unsigned long mask = BIT_MASK(nr);
+       unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+       unsigned long old = *p;
+
+       *p = old | mask;
+       return (old & mask) != 0;
+}
+
+/**
+ * __test_and_clear_bit - Clear a bit and return its old value
+ * @nr: Bit to clear
+ * @addr: Address to count from
+ *
+ * This operation is non-atomic and can be reordered.
+ * If two examples of this operation race, one can appear to succeed
+ * but actually fail.  You must protect multiple accesses with a lock.
+ */
+static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr)
+{
+       unsigned long mask = BIT_MASK(nr);
+       unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+       unsigned long old = *p;
+
+       *p = old & ~mask;
+       return (old & mask) != 0;
+}
+
+/* WARNING: non atomic and it can be reordered! */
+static inline int __test_and_change_bit(int nr,
+                                           volatile unsigned long *addr)
+{
+       unsigned long mask = BIT_MASK(nr);
+       unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+       unsigned long old = *p;
+
+       *p = old ^ mask;
+       return (old & mask) != 0;
+}
+
+/**
+ * test_bit - Determine whether a bit is set
+ * @nr: bit number to test
+ * @addr: Address to start counting from
+ */
+static inline int test_bit(int nr, const volatile unsigned long *addr)
+{
+       return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
+}
+
+#endif /* __ASM_SH_BITOPS_OP32_H */
index 367930d..ebe595b 100644 (file)
 
 #ifdef CONFIG_GUSA_RB
 #include <asm/bitops-grb.h>
+#elif defined(CONFIG_CPU_SH2A)
+#include <asm-generic/bitops/atomic.h>
+#include <asm/bitops-op32.h>
 #elif defined(CONFIG_CPU_SH4A)
 #include <asm/bitops-llsc.h>
 #else
-#include <asm/bitops-irq.h>
+#include <asm-generic/bitops/atomic.h>
+#include <asm-generic/bitops/non-atomic.h>
 #endif
 
-
 /*
  * clear_bit() doesn't provide any barrier for the compiler.
  */
 #define smp_mb__before_clear_bit()     barrier()
 #define smp_mb__after_clear_bit()      barrier()
 
-#include <asm-generic/bitops/non-atomic.h>
-
 #ifdef CONFIG_SUPERH32
 static inline unsigned long ffz(unsigned long word)
 {
index 121b2ec..4924ff6 100644 (file)
@@ -25,7 +25,7 @@ static void __init check_bugs(void)
        case CPU_SH7619:
                *p++ = '2';
                break;
-       case CPU_SH7203 ... CPU_MXG:
+       case CPU_SH7201 ... CPU_MXG:
                *p++ = '2';
                *p++ = 'a';
                break;
index 9eb9036..b809f22 100644 (file)
@@ -108,13 +108,11 @@ typedef struct user_fpu_struct elf_fpregset_t;
 #define elf_check_fdpic(x)             ((x)->e_flags & EF_SH_FDPIC)
 #define elf_check_const_displacement(x)        ((x)->e_flags & EF_SH_PIC)
 
-#ifdef CONFIG_SUPERH32
 /*
  * Enable dump using regset.
  * This covers all of general/DSP/FPU regs.
  */
 #define CORE_DUMP_USE_REGSET
-#endif
 
 #define USE_ELF_CORE_DUMP
 #define ELF_FDPIC_CORE_EFLAGS  EF_SH_FDPIC
index 3aed362..8fea7d8 100644 (file)
@@ -1,8 +1,34 @@
 #ifndef __ASM_SH_FTRACE_H
 #define __ASM_SH_FTRACE_H
 
+#ifdef CONFIG_FUNCTION_TRACER
+
+#define MCOUNT_INSN_SIZE       4 /* sizeof mcount call */
+
 #ifndef __ASSEMBLY__
 extern void mcount(void);
-#endif
+
+#define MCOUNT_ADDR            ((long)(mcount))
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+#define CALLER_ADDR            ((long)(ftrace_caller))
+#define STUB_ADDR              ((long)(ftrace_stub))
+
+#define MCOUNT_INSN_OFFSET     ((STUB_ADDR - CALLER_ADDR) >> 1)
+
+struct dyn_arch_ftrace {
+       /* No extra data needed on sh */
+};
+
+#endif /* CONFIG_DYNAMIC_FTRACE */
+
+static inline unsigned long ftrace_call_adjust(unsigned long addr)
+{
+       /* 'addr' is the memory table address. */
+       return addr;
+}
+
+#endif /* __ASSEMBLY__ */
+#endif /* CONFIG_FUNCTION_TRACER */
 
 #endif /* __ASM_SH_FTRACE_H */
index 65eaae3..61f6dae 100644 (file)
@@ -260,6 +260,10 @@ __ioremap_mode(unsigned long offset, unsigned long size, unsigned long flags)
 
                return (void __iomem *)P2SEGADDR(offset);
        }
+
+       /* P4 above the store queues are always mapped. */
+       if (unlikely(offset >= P3_ADDR_MAX))
+               return (void __iomem *)P4SEGADDR(offset);
 #endif
 
        return __ioremap(offset, size, flags);
index 24e4207..72704ed 100644 (file)
@@ -1,21 +1,7 @@
-/*
- * May be copied or modified under the terms of the GNU General Public
- * License.  See linux/COPYING for more information.
- *
- * Based on original code by Glenn Engel, Jim Kingdon,
- * David Grothe <dave@gcom.com>, Tigran Aivazian, <tigran@sco.com> and
- * Amit S. Kale <akale@veritas.com>
- * 
- * Super-H port based on sh-stub.c (Ben Lee and Steve Chamberlain) by
- * Henry Bell <henry.bell@st.com>
- * 
- * Header file for low-level support for remote debug using GDB. 
- *
- */
-
-#ifndef __KGDB_H
-#define __KGDB_H
+#ifndef __ASM_SH_KGDB_H
+#define __ASM_SH_KGDB_H
 
+#include <asm/cacheflush.h>
 #include <asm/ptrace.h>
 
 /* Same as pt_regs but has vbr in place of syscall_nr */
@@ -30,40 +16,26 @@ struct kgdb_regs {
         unsigned long vbr;
 };
 
-/* State info */
-extern char kgdb_in_gdb_mode;
-extern int kgdb_nofault;       /* Ignore bus errors (in gdb mem access) */
-extern char in_nmi;            /* Debounce flag to prevent NMI reentry*/
+enum regnames {
+       GDB_R0, GDB_R1, GDB_R2, GDB_R3, GDB_R4, GDB_R5, GDB_R6, GDB_R7,
+       GDB_R8, GDB_R9, GDB_R10, GDB_R11, GDB_R12, GDB_R13, GDB_R14, GDB_R15,
 
-/* SCI */
-extern int kgdb_portnum;
-extern int kgdb_baud;
-extern char kgdb_parity;
-extern char kgdb_bits;
+       GDB_PC, GDB_PR, GDB_SR, GDB_GBR, GDB_MACH, GDB_MACL, GDB_VBR,
+};
 
-/* Init and interface stuff */
-extern int kgdb_init(void);
-extern int (*kgdb_getchar)(void);
-extern void (*kgdb_putchar)(int);
+#define NUMREGBYTES    ((GDB_VBR + 1) * 4)
 
-/* Trap functions */
-typedef void (kgdb_debug_hook_t)(struct pt_regs *regs);
-typedef void (kgdb_bus_error_hook_t)(void);
-extern kgdb_debug_hook_t  *kgdb_debug_hook;
-extern kgdb_bus_error_hook_t *kgdb_bus_err_hook;
+static inline void arch_kgdb_breakpoint(void)
+{
+       __asm__ __volatile__ ("trapa #0x3c\n");
+}
 
-/* Console */
-struct console;
-void kgdb_console_write(struct console *co, const char *s, unsigned count);
-extern int kgdb_console_setup(struct console *, char *);
+/* State info */
+extern char in_nmi;            /* Debounce flag to prevent NMI reentry*/
 
-/* Prototypes for jmp fns */
-#define _JBLEN 9
-typedef        int jmp_buf[_JBLEN];
-extern void    longjmp(jmp_buf __jmpb, int __retval);
-extern int     setjmp(jmp_buf __jmpb);
+#define BUFMAX                 2048
 
-/* Forced breakpoint */
-#define breakpoint()   __asm__ __volatile__("trapa   #0x3c")
+#define CACHE_FLUSH_IS_SAFE    1
+#define BREAK_INSTR_SIZE       2
 
-#endif
+#endif /* __ASM_SH_KGDB_H */
index f1bae02..64b1c16 100644 (file)
@@ -14,8 +14,6 @@
 #include <linux/time.h>
 #include <asm/machtypes.h>
 
-struct device;
-
 struct sh_machine_vector {
        void (*mv_setup)(char **cmdline_p);
        const char *mv_name;
@@ -45,9 +43,6 @@ struct sh_machine_vector {
        int (*mv_irq_demux)(int irq);
 
        void (*mv_init_irq)(void);
-       void (*mv_init_pci)(void);
-
-       void (*mv_heartbeat)(void);
 
        void __iomem *(*mv_ioport_map)(unsigned long port, unsigned int size);
        void (*mv_ioport_unmap)(void __iomem *);
index 04c0c97..5d9157b 100644 (file)
@@ -22,7 +22,7 @@
 #define MMU_CONTEXT_ASID_MASK          0x000000ff
 #define MMU_CONTEXT_VERSION_MASK       0xffffff00
 #define MMU_CONTEXT_FIRST_VERSION      0x00000100
-#define NO_CONTEXT                     0
+#define NO_CONTEXT                     0UL
 
 /* ASID is 8-bit value, so it can't be 0x100 */
 #define MMU_NO_ASID                    0x100
@@ -130,7 +130,7 @@ static inline void switch_mm(struct mm_struct *prev,
 #define destroy_context(mm)            do { } while (0)
 #define set_asid(asid)                 do { } while (0)
 #define get_asid()                     (0)
-#define cpu_asid(cpu, mm)              ({ (void)cpu; 0; })
+#define cpu_asid(cpu, mm)              ({ (void)cpu; NO_CONTEXT; })
 #define switch_and_save_asid(asid)     (0)
 #define set_TTB(pgd)                   do { } while (0)
 #define get_TTB()                      (0)
diff --git a/arch/sh/include/asm/mutex-llsc.h b/arch/sh/include/asm/mutex-llsc.h
new file mode 100644 (file)
index 0000000..ee839ee
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * arch/sh/include/asm/mutex-llsc.h
+ *
+ * SH-4A optimized mutex locking primitives
+ *
+ * Please look into asm-generic/mutex-xchg.h for a formal definition.
+ */
+#ifndef __ASM_SH_MUTEX_LLSC_H
+#define __ASM_SH_MUTEX_LLSC_H
+
+/*
+ * Attempting to lock a mutex on SH4A is done like in ARMv6+ architecure.
+ * with a bastardized atomic decrement (it is not a reliable atomic decrement
+ * but it satisfies the defined semantics for our purpose, while being
+ * smaller and faster than a real atomic decrement or atomic swap.
+ * The idea is to attempt  decrementing the lock value only once. If once
+ * decremented it isn't zero, or if its store-back fails due to a dispute
+ * on the exclusive store, we simply bail out immediately through the slow
+ * path where the lock will be reattempted until it succeeds.
+ */
+static inline void
+__mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
+{
+       int __ex_flag, __res;
+
+       __asm__ __volatile__ (
+               "movli.l        @%2, %0 \n"
+               "add            #-1, %0 \n"
+               "movco.l        %0, @%2 \n"
+               "movt           %1      \n"
+               : "=&z" (__res), "=&r" (__ex_flag)
+               : "r" (&(count)->counter)
+               : "t");
+
+       __res |= !__ex_flag;
+       if (unlikely(__res != 0))
+               fail_fn(count);
+}
+
+static inline int
+__mutex_fastpath_lock_retval(atomic_t *count, int (*fail_fn)(atomic_t *))
+{
+       int __ex_flag, __res;
+
+       __asm__ __volatile__ (
+               "movli.l        @%2, %0 \n"
+               "add            #-1, %0 \n"
+               "movco.l        %0, @%2 \n"
+               "movt           %1      \n"
+               : "=&z" (__res), "=&r" (__ex_flag)
+               : "r" (&(count)->counter)
+               : "t");
+
+       __res |= !__ex_flag;
+       if (unlikely(__res != 0))
+               __res = fail_fn(count);
+
+       return __res;
+}
+
+static inline void
+__mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *))
+{
+       int __ex_flag, __res;
+
+       __asm__ __volatile__ (
+               "movli.l        @%2, %0 \n\t"
+               "add            #1, %0  \n\t"
+               "movco.l        %0, @%2 \n\t"
+               "movt           %1      \n\t"
+               : "=&z" (__res), "=&r" (__ex_flag)
+               : "r" (&(count)->counter)
+               : "t");
+
+       __res |= !__ex_flag;
+       if (unlikely(__res <= 0))
+               fail_fn(count);
+}
+
+/*
+ * If the unlock was done on a contended lock, or if the unlock simply fails
+ * then the mutex remains locked.
+ */
+#define __mutex_slowpath_needs_to_unlock()     1
+
+/*
+ * For __mutex_fastpath_trylock we do an atomic decrement and check the
+ * result and put it in the __res variable.
+ */
+static inline int
+__mutex_fastpath_trylock(atomic_t *count, int (*fail_fn)(atomic_t *))
+{
+       int __res, __orig;
+
+       __asm__ __volatile__ (
+               "1: movli.l     @%2, %0         \n\t"
+               "dt             %0              \n\t"
+               "movco.l        %0,@%2          \n\t"
+               "bf             1b              \n\t"
+               "cmp/eq         #0,%0           \n\t"
+               "bt             2f              \n\t"
+               "mov            #0, %1          \n\t"
+               "bf             3f              \n\t"
+               "2: mov         #1, %1          \n\t"
+               "3:                             "
+               : "=&z" (__orig), "=&r" (__res)
+               : "r" (&count->counter)
+               : "t");
+
+       return __res;
+}
+#endif /* __ASM_SH_MUTEX_LLSC_H */
index 458c1f7..d8e3771 100644 (file)
@@ -5,5 +5,8 @@
  * implementation in place, or pick the atomic_xchg() based generic
  * implementation. (see asm-generic/mutex-xchg.h for details)
  */
-
+#if defined(CONFIG_CPU_SH4A)
+#include <asm/mutex-llsc.h>
+#else
 #include <asm-generic/mutex-dec.h>
+#endif
diff --git a/arch/sh/include/asm/pm.h b/arch/sh/include/asm/pm.h
deleted file mode 100644 (file)
index 56fdbd6..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright 2006 (c) Andriy Skulysh <askulysh@gmail.com>
- *
- */
-#ifndef __ASM_SH_PM_H
-#define __ASM_SH_PM_H
-
-extern u8 wakeup_start;
-extern u8 wakeup_end;
-
-void pm_enter(void);
-
-#endif
index 693364a..1ef4b24 100644 (file)
@@ -18,7 +18,7 @@ enum cpu_type {
        CPU_SH7619,
 
        /* SH-2A types */
-       CPU_SH7203, CPU_SH7206, CPU_SH7263, CPU_MXG,
+       CPU_SH7201, CPU_SH7203, CPU_SH7206, CPU_SH7263, CPU_MXG,
 
        /* SH-3 types */
        CPU_SH7705, CPU_SH7706, CPU_SH7707,
@@ -82,6 +82,9 @@ extern struct sh_cpuinfo cpu_data[];
 #define current_cpu_data cpu_data[smp_processor_id()]
 #define raw_current_cpu_data cpu_data[raw_smp_processor_id()]
 
+#define cpu_sleep()    __asm__ __volatile__ ("sleep" : : : "memory")
+#define cpu_relax()    barrier()
+
 /* Forward decl */
 struct seq_operations;
 
index a46a020..d79063c 100644 (file)
@@ -175,6 +175,15 @@ static __inline__ void enable_fpu(void)
 
 void show_trace(struct task_struct *tsk, unsigned long *sp,
                struct pt_regs *regs);
+
+#ifdef CONFIG_DUMP_CODE
+void show_code(struct pt_regs *regs);
+#else
+static inline void show_code(struct pt_regs *regs)
+{
+}
+#endif
+
 extern unsigned long get_wchan(struct task_struct *p);
 
 #define KSTK_EIP(tsk)  (task_pt_regs(tsk)->pc)
@@ -182,9 +191,6 @@ extern unsigned long get_wchan(struct task_struct *p);
 
 #define user_stack_pointer(regs)       ((regs)->regs[15])
 
-#define cpu_sleep()    __asm__ __volatile__ ("sleep" : : : "memory")
-#define cpu_relax()    barrier()
-
 #if defined(CONFIG_CPU_SH2A) || defined(CONFIG_CPU_SH3) || \
     defined(CONFIG_CPU_SH4)
 #define PREFETCH_STRIDE                L1_CACHE_BYTES
index b0b4824..803177f 100644 (file)
@@ -226,9 +226,7 @@ extern unsigned long get_wchan(struct task_struct *p);
 #define KSTK_EIP(tsk)  ((tsk)->thread.pc)
 #define KSTK_ESP(tsk)  ((tsk)->thread.sp)
 
-#define user_stack_pointer(regs)       ((regs)->sp)
-
-#define cpu_relax()    barrier()
+#define user_stack_pointer(regs)       ((regs)->regs[15])
 
 #endif /* __ASSEMBLY__ */
 #endif /* __ASM_SH_PROCESSOR_64_H */
index 3ad18e9..12912ab 100644 (file)
@@ -86,6 +86,7 @@ struct pt_dspregs {
        unsigned long   re;
        unsigned long   mod;
 };
+#endif
 
 #define PTRACE_GETREGS         12      /* General registers */
 #define PTRACE_SETREGS         13
@@ -100,7 +101,6 @@ struct pt_dspregs {
 
 #define        PTRACE_GETDSPREGS       55      /* DSP registers */
 #define        PTRACE_SETDSPREGS       56
-#endif
 
 #ifdef __KERNEL__
 #include <asm/addrspace.h>
index 0ca2619..d9c96d7 100644 (file)
@@ -10,7 +10,6 @@
 
 extern void sh_bios_console_write(const char *buf, unsigned int len);
 extern void sh_bios_char_out(char ch);
-extern int sh_bios_in_gdb_mode(void);
 extern void sh_bios_gdb_detach(void);
 
 extern void sh_bios_get_node_addr(unsigned char *node_addr);
index aa1fef2..7420071 100644 (file)
@@ -1,17 +1,20 @@
 #ifndef __ASM_SH_STRING_64_H
 #define __ASM_SH_STRING_64_H
 
-/*
- * include/asm-sh/string_64.h
- *
- * Copyright (C) 2000, 2001  Paolo Alberelli
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
+#ifdef __KERNEL__
+
+#define __HAVE_ARCH_MEMSET
+extern void *memset(void *__s, int __c, size_t __count);
 
 #define __HAVE_ARCH_MEMCPY
 extern void *memcpy(void *dest, const void *src, size_t count);
 
+#define __HAVE_ARCH_STRLEN
+extern size_t strlen(const char *);
+
+#define __HAVE_ARCH_STRCPY
+extern char *strcpy(char *__dest, const char *__src);
+
+#endif /* __KERNEL__ */
+
 #endif /* __ASM_SH_STRING_64_H */
index 54773f2..05a868a 100644 (file)
@@ -5,7 +5,7 @@
 #include <linux/sched.h>
 #include <asm/ptrace.h>
 
-/* The system call number is given by the user in %g1 */
+/* The system call number is given by the user in R3 */
 static inline long syscall_get_nr(struct task_struct *task,
                                  struct pt_regs *regs)
 {
index bcaaa8c..e1143b9 100644 (file)
@@ -1,6 +1,80 @@
 #ifndef __ASM_SH_SYSCALL_64_H
 #define __ASM_SH_SYSCALL_64_H
 
-#include <asm-generic/syscall.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/ptrace.h>
+
+/* The system call number is given by the user in R9 */
+static inline long syscall_get_nr(struct task_struct *task,
+                                 struct pt_regs *regs)
+{
+       return (regs->syscall_nr >= 0) ? regs->regs[9] : -1L;
+}
+
+static inline void syscall_rollback(struct task_struct *task,
+                                   struct pt_regs *regs)
+{
+       /*
+        * XXX: This needs some thought. On SH we don't
+        * save away the original R9 value anywhere.
+        */
+}
+
+static inline bool syscall_has_error(struct pt_regs *regs)
+{
+       return (regs->sr & 0x1) ? true : false;
+}
+static inline void syscall_set_error(struct pt_regs *regs)
+{
+       regs->sr |= 0x1;
+}
+static inline void syscall_clear_error(struct pt_regs *regs)
+{
+       regs->sr &= ~0x1;
+}
+
+static inline long syscall_get_error(struct task_struct *task,
+                                    struct pt_regs *regs)
+{
+       return syscall_has_error(regs) ? regs->regs[9] : 0;
+}
+
+static inline long syscall_get_return_value(struct task_struct *task,
+                                           struct pt_regs *regs)
+{
+       return regs->regs[9];
+}
+
+static inline void syscall_set_return_value(struct task_struct *task,
+                                           struct pt_regs *regs,
+                                           int error, long val)
+{
+       if (error) {
+               syscall_set_error(regs);
+               regs->regs[9] = -error;
+       } else {
+               syscall_clear_error(regs);
+               regs->regs[9] = val;
+       }
+}
+
+static inline void syscall_get_arguments(struct task_struct *task,
+                                        struct pt_regs *regs,
+                                        unsigned int i, unsigned int n,
+                                        unsigned long *args)
+{
+       BUG_ON(i + n > 6);
+       memcpy(args, &regs->regs[2 + i], n * sizeof(args[0]));
+}
+
+static inline void syscall_set_arguments(struct task_struct *task,
+                                        struct pt_regs *regs,
+                                        unsigned int i, unsigned int n,
+                                        const unsigned long *args)
+{
+       BUG_ON(i + n > 6);
+       memcpy(&regs->regs[2 + i], args, n * sizeof(args[0]));
+}
 
 #endif /* __ASM_SH_SYSCALL_64_H */
index 6160fe4..c9ec6af 100644 (file)
@@ -175,6 +175,8 @@ asmlinkage void name##_trap_handler(unsigned int vec, struct pt_regs *regs)
 BUILD_TRAP_HANDLER(address_error);
 BUILD_TRAP_HANDLER(debug);
 BUILD_TRAP_HANDLER(bug);
+BUILD_TRAP_HANDLER(breakpoint);
+BUILD_TRAP_HANDLER(singlestep);
 BUILD_TRAP_HANDLER(fpu_error);
 BUILD_TRAP_HANDLER(fpu_state_restore);
 
diff --git a/arch/sh/include/asm/unaligned-sh4a.h b/arch/sh/include/asm/unaligned-sh4a.h
new file mode 100644 (file)
index 0000000..d8f8977
--- /dev/null
@@ -0,0 +1,258 @@
+#ifndef __ASM_SH_UNALIGNED_SH4A_H
+#define __ASM_SH_UNALIGNED_SH4A_H
+
+/*
+ * SH-4A has support for unaligned 32-bit loads, and 32-bit loads only.
+ * Support for 16 and 64-bit accesses are done through shifting and
+ * masking relative to the endianness. Unaligned stores are not supported
+ * by the instruction encoding, so these continue to use the packed
+ * struct.
+ *
+ * The same note as with the movli.l/movco.l pair applies here, as long
+ * as the load is gauranteed to be inlined, nothing else will hook in to
+ * r0 and we get the return value for free.
+ *
+ * NOTE: Due to the fact we require r0 encoding, care should be taken to
+ * avoid mixing these heavily with other r0 consumers, such as the atomic
+ * ops. Failure to adhere to this can result in the compiler running out
+ * of spill registers and blowing up when building at low optimization
+ * levels. See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34777.
+ */
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+static __always_inline u32 __get_unaligned_cpu32(const u8 *p)
+{
+       unsigned long unaligned;
+
+       __asm__ __volatile__ (
+               "movua.l        @%1, %0\n\t"
+                : "=z" (unaligned)
+                : "r" (p)
+       );
+
+       return unaligned;
+}
+
+struct __una_u16 { u16 x __attribute__((packed)); };
+struct __una_u32 { u32 x __attribute__((packed)); };
+struct __una_u64 { u64 x __attribute__((packed)); };
+
+static inline u16 __get_unaligned_cpu16(const u8 *p)
+{
+#ifdef __LITTLE_ENDIAN
+       return __get_unaligned_cpu32(p) & 0xffff;
+#else
+       return __get_unaligned_cpu32(p) >> 16;
+#endif
+}
+
+/*
+ * Even though movua.l supports auto-increment on the read side, it can
+ * only store to r0 due to instruction encoding constraints, so just let
+ * the compiler sort it out on its own.
+ */
+static inline u64 __get_unaligned_cpu64(const u8 *p)
+{
+#ifdef __LITTLE_ENDIAN
+       return (u64)__get_unaligned_cpu32(p + 4) << 32 |
+                   __get_unaligned_cpu32(p);
+#else
+       return (u64)__get_unaligned_cpu32(p) << 32 |
+                   __get_unaligned_cpu32(p + 4);
+#endif
+}
+
+static inline u16 get_unaligned_le16(const void *p)
+{
+       return le16_to_cpu(__get_unaligned_cpu16(p));
+}
+
+static inline u32 get_unaligned_le32(const void *p)
+{
+       return le32_to_cpu(__get_unaligned_cpu32(p));
+}
+
+static inline u64 get_unaligned_le64(const void *p)
+{
+       return le64_to_cpu(__get_unaligned_cpu64(p));
+}
+
+static inline u16 get_unaligned_be16(const void *p)
+{
+       return be16_to_cpu(__get_unaligned_cpu16(p));
+}
+
+static inline u32 get_unaligned_be32(const void *p)
+{
+       return be32_to_cpu(__get_unaligned_cpu32(p));
+}
+
+static inline u64 get_unaligned_be64(const void *p)
+{
+       return be64_to_cpu(__get_unaligned_cpu64(p));
+}
+
+static inline void __put_le16_noalign(u8 *p, u16 val)
+{
+       *p++ = val;
+       *p++ = val >> 8;
+}
+
+static inline void __put_le32_noalign(u8 *p, u32 val)
+{
+       __put_le16_noalign(p, val);
+       __put_le16_noalign(p + 2, val >> 16);
+}
+
+static inline void __put_le64_noalign(u8 *p, u64 val)
+{
+       __put_le32_noalign(p, val);
+       __put_le32_noalign(p + 4, val >> 32);
+}
+
+static inline void __put_be16_noalign(u8 *p, u16 val)
+{
+       *p++ = val >> 8;
+       *p++ = val;
+}
+
+static inline void __put_be32_noalign(u8 *p, u32 val)
+{
+       __put_be16_noalign(p, val >> 16);
+       __put_be16_noalign(p + 2, val);
+}
+
+static inline void __put_be64_noalign(u8 *p, u64 val)
+{
+       __put_be32_noalign(p, val >> 32);
+       __put_be32_noalign(p + 4, val);
+}
+
+static inline void put_unaligned_le16(u16 val, void *p)
+{
+#ifdef __LITTLE_ENDIAN
+       ((struct __una_u16 *)p)->x = val;
+#else
+       __put_le16_noalign(p, val);
+#endif
+}
+
+static inline void put_unaligned_le32(u32 val, void *p)
+{
+#ifdef __LITTLE_ENDIAN
+       ((struct __una_u32 *)p)->x = val;
+#else
+       __put_le32_noalign(p, val);
+#endif
+}
+
+static inline void put_unaligned_le64(u64 val, void *p)
+{
+#ifdef __LITTLE_ENDIAN
+       ((struct __una_u64 *)p)->x = val;
+#else
+       __put_le64_noalign(p, val);
+#endif
+}
+
+static inline void put_unaligned_be16(u16 val, void *p)
+{
+#ifdef __BIG_ENDIAN
+       ((struct __una_u16 *)p)->x = val;
+#else
+       __put_be16_noalign(p, val);
+#endif
+}
+
+static inline void put_unaligned_be32(u32 val, void *p)
+{
+#ifdef __BIG_ENDIAN
+       ((struct __una_u32 *)p)->x = val;
+#else
+       __put_be32_noalign(p, val);
+#endif
+}
+
+static inline void put_unaligned_be64(u64 val, void *p)
+{
+#ifdef __BIG_ENDIAN
+       ((struct __una_u64 *)p)->x = val;
+#else
+       __put_be64_noalign(p, val);
+#endif
+}
+
+/*
+ * Cause a link-time error if we try an unaligned access other than
+ * 1,2,4 or 8 bytes long
+ */
+extern void __bad_unaligned_access_size(void);
+
+#define __get_unaligned_le(ptr) ((__force typeof(*(ptr)))({                    \
+       __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr),                      \
+       __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_le16((ptr)),   \
+       __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_le32((ptr)),   \
+       __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_le64((ptr)),   \
+       __bad_unaligned_access_size()))));                                      \
+       }))
+
+#define __get_unaligned_be(ptr) ((__force typeof(*(ptr)))({                    \
+       __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr),                      \
+       __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_be16((ptr)),   \
+       __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_be32((ptr)),   \
+       __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_be64((ptr)),   \
+       __bad_unaligned_access_size()))));                                      \
+       }))
+
+#define __put_unaligned_le(val, ptr) ({                                        \
+       void *__gu_p = (ptr);                                           \
+       switch (sizeof(*(ptr))) {                                       \
+       case 1:                                                         \
+               *(u8 *)__gu_p = (__force u8)(val);                      \
+               break;                                                  \
+       case 2:                                                         \
+               put_unaligned_le16((__force u16)(val), __gu_p);         \
+               break;                                                  \
+       case 4:                                                         \
+               put_unaligned_le32((__force u32)(val), __gu_p);         \
+               break;                                                  \
+       case 8:                                                         \
+               put_unaligned_le64((__force u64)(val), __gu_p);         \
+               break;                                                  \
+       default:                                                        \
+               __bad_unaligned_access_size();                          \
+               break;                                                  \
+       }                                                               \
+       (void)0; })
+
+#define __put_unaligned_be(val, ptr) ({                                        \
+       void *__gu_p = (ptr);                                           \
+       switch (sizeof(*(ptr))) {                                       \
+       case 1:                                                         \
+               *(u8 *)__gu_p = (__force u8)(val);                      \
+               break;                                                  \
+       case 2:                                                         \
+               put_unaligned_be16((__force u16)(val), __gu_p);         \
+               break;                                                  \
+       case 4:                                                         \
+               put_unaligned_be32((__force u32)(val), __gu_p);         \
+               break;                                                  \
+       case 8:                                                         \
+               put_unaligned_be64((__force u64)(val), __gu_p);         \
+               break;                                                  \
+       default:                                                        \
+               __bad_unaligned_access_size();                          \
+               break;                                                  \
+       }                                                               \
+       (void)0; })
+
+#ifdef __LITTLE_ENDIAN
+# define get_unaligned __get_unaligned_le
+# define put_unaligned __put_unaligned_le
+#else
+# define get_unaligned __get_unaligned_be
+# define put_unaligned __put_unaligned_be
+#endif
+
+#endif /* __ASM_SH_UNALIGNED_SH4A_H */
index c1641a0..8c0ad5e 100644 (file)
@@ -1,7 +1,11 @@
 #ifndef _ASM_SH_UNALIGNED_H
 #define _ASM_SH_UNALIGNED_H
 
-/* SH can't handle unaligned accesses. */
+#ifdef CONFIG_CPU_SH4A
+/* SH-4A can handle unaligned loads in a relatively neutered fashion. */
+#include <asm/unaligned-sh4a.h>
+#else
+/* Otherwise, SH can't handle unaligned accesses. */
 #ifdef __LITTLE_ENDIAN__
 # include <linux/unaligned/le_struct.h>
 # include <linux/unaligned/be_byteshift.h>
@@ -15,5 +19,6 @@
 # define get_unaligned __get_unaligned_be
 # define put_unaligned __put_unaligned_be
 #endif
+#endif
 
 #endif /* _ASM_SH_UNALIGNED_H */
index 4e53eb3..9a22b88 100644 (file)
 #define PORT_PSELC     0xA4050128UL
 #define PORT_PSELD     0xA405012AUL
 
+#elif defined(CONFIG_CPU_SUBTYPE_SH7709)
+
+/* Control registers */
+#define PORT_PACR       0xa4000100UL
+#define PORT_PBCR       0xa4000102UL
+#define PORT_PCCR       0xa4000104UL
+#define PORT_PFCR       0xa400010aUL
+
+/* Data registers */
+#define PORT_PADR       0xa4000120UL
+#define PORT_PBDR       0xa4000122UL
+#define PORT_PCDR       0xa4000124UL
+#define PORT_PFDR       0xa400012aUL
+
 #endif
 
 #endif
index 5bdc9d9..efc43b3 100644 (file)
@@ -1,30 +1,7 @@
-/*
- * include/asm-sh/edosk7705.h
- *
- * Modified version of io_se.h for the EDOSK7705 specific functions.
- *
- * May be copied or modified under the terms of the GNU General Public
- * License.  See linux/COPYING for more information.
- *
- * IO functions for an Hitachi EDOSK7705 development board
- */
-
-#ifndef __ASM_SH_EDOSK7705_IO_H
-#define __ASM_SH_EDOSK7705_IO_H
+#ifndef __ASM_SH_EDOSK7705_H
+#define __ASM_SH_EDOSK7705_H
 
+#define __IO_PREFIX sh_edosk7705
 #include <asm/io_generic.h>
 
-extern unsigned char sh_edosk7705_inb(unsigned long port);
-extern unsigned int sh_edosk7705_inl(unsigned long port);
-
-extern void sh_edosk7705_outb(unsigned char value, unsigned long port);
-extern void sh_edosk7705_outl(unsigned int value, unsigned long port);
-
-extern void sh_edosk7705_insb(unsigned long port, void *addr, unsigned long count);
-extern void sh_edosk7705_insl(unsigned long port, void *addr, unsigned long count);
-extern void sh_edosk7705_outsb(unsigned long port, const void *addr, unsigned long count);
-extern void sh_edosk7705_outsl(unsigned long port, const void *addr, unsigned long count);
-
-extern unsigned long sh_edosk7705_isa_port2addr(unsigned long offset);
-
-#endif /* __ASM_SH_EDOSK7705_IO_H */
+#endif /* __ASM_SH_EDOSK7705_H */
diff --git a/arch/sh/include/mach-se/mach/mrshpc.h b/arch/sh/include/mach-se/mach/mrshpc.h
new file mode 100644 (file)
index 0000000..56287ee
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef __MACH_SE_MRSHPC_H
+#define __MACH_SE_MRSHPC_H
+
+#include <linux/io.h>
+
+static inline void __init mrshpc_setup_windows(void)
+{
+       if ((__raw_readw(MRSHPC_CSR) & 0x000c) != 0)
+               return; /* Not detected */
+
+       if ((__raw_readw(MRSHPC_CSR) & 0x0080) == 0) {
+               __raw_writew(0x0674, MRSHPC_CPWCR); /* Card Vcc is 3.3v? */
+       } else {
+               __raw_writew(0x0678, MRSHPC_CPWCR); /* Card Vcc is 5V */
+       }
+
+       /*
+        *  PC-Card window open
+        *  flag == COMMON/ATTRIBUTE/IO
+        */
+       /* common window open */
+       __raw_writew(0x8a84, MRSHPC_MW0CR1);
+       if((__raw_readw(MRSHPC_CSR) & 0x4000) != 0)
+               /* common mode & bus width 16bit SWAP = 1*/
+               __raw_writew(0x0b00, MRSHPC_MW0CR2);
+       else
+               /* common mode & bus width 16bit SWAP = 0*/
+               __raw_writew(0x0300, MRSHPC_MW0CR2);
+
+       /* attribute window open */
+       __raw_writew(0x8a85, MRSHPC_MW1CR1);
+       if ((__raw_readw(MRSHPC_CSR) & 0x4000) != 0)
+               /* attribute mode & bus width 16bit SWAP = 1*/
+               __raw_writew(0x0a00, MRSHPC_MW1CR2);
+       else
+               /* attribute mode & bus width 16bit SWAP = 0*/
+               __raw_writew(0x0200, MRSHPC_MW1CR2);
+
+       /* I/O window open */
+       __raw_writew(0x8a86, MRSHPC_IOWCR1);
+       __raw_writew(0x0008, MRSHPC_CDCR);       /* I/O card mode */
+       if ((__raw_readw(MRSHPC_CSR) & 0x4000) != 0)
+               __raw_writew(0x0a00, MRSHPC_IOWCR2); /* bus width 16bit SWAP = 1*/
+       else
+               __raw_writew(0x0200, MRSHPC_IOWCR2); /* bus width 16bit SWAP = 0*/
+
+       __raw_writew(0x2000, MRSHPC_ICR);
+       __raw_writeb(0x00, PA_MRSHPC_MW2 + 0x206);
+       __raw_writeb(0x42, PA_MRSHPC_MW2 + 0x200);
+}
+
+#endif /* __MACH_SE_MRSHPC_H */
index eb23000..14be91c 100644 (file)
 #define BCR_ILCRF      (PA_BCR + 10)
 #define BCR_ILCRG      (PA_BCR + 12)
 
+#if defined(CONFIG_CPU_SUBTYPE_SH7709)
+#define INTC_IRR0       0xa4000004UL
+#define INTC_IRR1       0xa4000006UL
+#define INTC_IRR2       0xa4000008UL
+
+#define INTC_ICR0       0xfffffee0UL
+#define INTC_ICR1       0xa4000010UL
+#define INTC_ICR2       0xa4000012UL
+#define INTC_INTER      0xa4000014UL
+
+#define INTC_IPRC       0xa4000016UL
+#define INTC_IPRD       0xa4000018UL
+#define INTC_IPRE       0xa400001aUL
+
+#define IRQ0_IRQ        32
+#define IRQ1_IRQ        33
+#endif
+
 #if defined(CONFIG_CPU_SUBTYPE_SH7705)
 #define IRQ_STNIC      12
 #define IRQ_CFCARD     14
index 9845846..749914b 100644 (file)
 #define FPGA_IN                0xb1400000
 #define FPGA_OUT       0xb1400002
 
-#define __IO_PREFIX    sh7343se
-#include <asm/io_generic.h>
-
 #define IRQ0_IRQ        32
 #define IRQ1_IRQ        33
 #define IRQ4_IRQ        36
 #define SE7343_FPGA_IRQ_MRSHPC3        3
 #define SE7343_FPGA_IRQ_SMC    6       /* EXT_IRQ2 */
 #define SE7343_FPGA_IRQ_USB    8
+#define SE7343_FPGA_IRQ_UARTA  10
+#define SE7343_FPGA_IRQ_UARTB  11
 
-#define SE7343_FPGA_IRQ_NR     11
+#define SE7343_FPGA_IRQ_NR     12
 #define SE7343_FPGA_IRQ_BASE   120
 
 #define MRSHPC_IRQ3            (SE7343_FPGA_IRQ_BASE + SE7343_FPGA_IRQ_MRSHPC3)
 #define MRSHPC_IRQ0            (SE7343_FPGA_IRQ_BASE + SE7343_FPGA_IRQ_MRSHPC0)
 #define SMC_IRQ                (SE7343_FPGA_IRQ_BASE + SE7343_FPGA_IRQ_SMC)
 #define USB_IRQ                (SE7343_FPGA_IRQ_BASE + SE7343_FPGA_IRQ_USB)
+#define UARTA_IRQ      (SE7343_FPGA_IRQ_BASE + SE7343_FPGA_IRQ_UARTA)
+#define UARTB_IRQ      (SE7343_FPGA_IRQ_BASE + SE7343_FPGA_IRQ_UARTB)
 
 /* arch/sh/boards/se/7343/irq.c */
 void init_7343se_IRQ(void);
index 48edfb1..2e1b86e 100644 (file)
@@ -4,25 +4,31 @@
 
 extra-y        := head_32.o init_task.o vmlinux.lds
 
-obj-y  := debugtraps.o io.o io_generic.o irq.o machvec.o process_32.o \
-          ptrace_32.o setup.o signal_32.o sys_sh.o sys_sh32.o \
-          syscalls_32.o time_32.o topology.o traps.o traps_32.o
+ifdef CONFIG_FUNCTION_TRACER
+# Do not profile debug and lowlevel utilities
+CFLAGS_REMOVE_ftrace.o = -pg
+endif
+
+obj-y  := debugtraps.o idle.o io.o io_generic.o irq.o                  \
+          machvec.o process_32.o ptrace_32.o setup.o signal_32.o       \
+          sys_sh.o sys_sh32.o syscalls_32.o time_32.o topology.o       \
+          traps.o traps_32.o
 
 obj-y                          += cpu/ timers/
 obj-$(CONFIG_VSYSCALL)         += vsyscall/
 obj-$(CONFIG_SMP)              += smp.o
-obj-$(CONFIG_CF_ENABLER)       += cf-enabler.o
 obj-$(CONFIG_SH_STANDARD_BIOS) += sh_bios.o
-obj-$(CONFIG_SH_KGDB)          += kgdb_stub.o kgdb_jmp.o
+obj-$(CONFIG_KGDB)             += kgdb.o
 obj-$(CONFIG_SH_CPU_FREQ)      += cpufreq.o
 obj-$(CONFIG_MODULES)          += sh_ksyms_32.o module.o
 obj-$(CONFIG_EARLY_PRINTK)     += early_printk.o
 obj-$(CONFIG_KEXEC)            += machine_kexec.o relocate_kernel.o
 obj-$(CONFIG_CRASH_DUMP)       += crash_dump.o
-obj-$(CONFIG_PM)               += pm.o
 obj-$(CONFIG_STACKTRACE)       += stacktrace.o
 obj-$(CONFIG_IO_TRAPPED)       += io_trapped.o
 obj-$(CONFIG_KPROBES)          += kprobes.o
 obj-$(CONFIG_GENERIC_GPIO)     += gpio.o
+obj-$(CONFIG_DYNAMIC_FTRACE)   += ftrace.o
+obj-$(CONFIG_DUMP_CODE)                += disassemble.o
 
 EXTRA_CFLAGS += -Werror
index c97660b..fe425d7 100644 (file)
@@ -1,21 +1,18 @@
 extra-y        := head_64.o init_task.o vmlinux.lds
 
-obj-y  := debugtraps.o io.o io_generic.o irq.o machvec.o process_64.o \
+obj-y  := debugtraps.o idle.o io.o io_generic.o irq.o machvec.o process_64.o \
           ptrace_64.o setup.o signal_64.o sys_sh.o sys_sh64.o \
           syscalls_64.o time_64.o topology.o traps.o traps_64.o
 
 obj-y                          += cpu/ timers/
 obj-$(CONFIG_VSYSCALL)         += vsyscall/
 obj-$(CONFIG_SMP)              += smp.o
-obj-$(CONFIG_CF_ENABLER)       += cf-enabler.o
 obj-$(CONFIG_SH_STANDARD_BIOS) += sh_bios.o
-obj-$(CONFIG_SH_KGDB)          += kgdb_stub.o kgdb_jmp.o
 obj-$(CONFIG_SH_CPU_FREQ)      += cpufreq.o
 obj-$(CONFIG_MODULES)          += sh_ksyms_64.o module.o
 obj-$(CONFIG_EARLY_PRINTK)     += early_printk.o
 obj-$(CONFIG_KEXEC)            += machine_kexec.o relocate_kernel.o
 obj-$(CONFIG_CRASH_DUMP)       += crash_dump.o
-obj-$(CONFIG_PM)               += pm.o
 obj-$(CONFIG_STACKTRACE)       += stacktrace.o
 obj-$(CONFIG_IO_TRAPPED)       += io_trapped.o
 obj-$(CONFIG_GENERIC_GPIO)     += gpio.o
diff --git a/arch/sh/kernel/cf-enabler.c b/arch/sh/kernel/cf-enabler.c
deleted file mode 100644 (file)
index bea4033..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-/* $Id: cf-enabler.c,v 1.4 2004/02/22 22:44:36 kkojima Exp $
- *
- *  linux/drivers/block/cf-enabler.c
- *
- *  Copyright (C) 1999  Niibe Yutaka
- *  Copyright (C) 2000  Toshiharu Nozawa
- *  Copyright (C) 2001  A&D Co., Ltd.
- *
- *  Enable the CF configuration.
- */
-
-#include <linux/init.h>
-#include <linux/mm.h>
-#include <linux/vmalloc.h>
-#include <linux/interrupt.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-
-/*
- * You can connect Compact Flash directly to the bus of SuperH.
- * This is the enabler for that.
- *
- * SIM: How generic is this really? It looks pretty board, or at
- * least SH sub-type, specific to me.
- * I know it doesn't work on the Overdrive!
- */
-
-/*
- * 0xB8000000 : Attribute
- * 0xB8001000 : Common Memory
- * 0xBA000000 : I/O
- */
-#if defined(CONFIG_CPU_SH4)
-/* SH4 can't access PCMCIA interface through P2 area.
- * we must remap it with appropriate attribute bit of the page set.
- * this part is based on Greg Banks' hd64465_ss.c implementation - Masahiro Abe */
-
-#if defined(CONFIG_CF_AREA6)
-#define slot_no 0
-#else
-#define slot_no 1
-#endif
-
-/* use this pointer to access to directly connected compact flash io area*/
-void *cf_io_base;
-
-static int __init allocate_cf_area(void)
-{
-       pgprot_t prot;
-       unsigned long paddrbase, psize;
-
-       /* open I/O area window */
-       paddrbase = virt_to_phys((void*)CONFIG_CF_BASE_ADDR);
-       psize = PAGE_SIZE;
-       prot = PAGE_KERNEL_PCC(slot_no, _PAGE_PCC_IO16);
-       cf_io_base = p3_ioremap(paddrbase, psize, prot.pgprot);
-       if (!cf_io_base) {
-               printk("allocate_cf_area : can't open CF I/O window!\n");
-               return -ENOMEM;
-       }
-/*     printk("p3_ioremap(paddr=0x%08lx, psize=0x%08lx, prot=0x%08lx)=0x%08lx\n",
-               paddrbase, psize, prot.pgprot, cf_io_base);*/
-
-       /* XXX : do we need attribute and common-memory area also? */
-
-       return 0;
-}
-#endif
-
-static int __init cf_init_default(void)
-{
-/* You must have enabled the card, and set the level interrupt
- * before reaching this point. Possibly in boot ROM or boot loader.
- */
-#if defined(CONFIG_CPU_SH4)
-       allocate_cf_area();
-#endif
-
-       return 0;
-}
-
-#if defined(CONFIG_SH_SOLUTION_ENGINE)
-#include <mach-se/mach/se.h>
-#elif defined(CONFIG_SH_7722_SOLUTION_ENGINE)
-#include <mach-se/mach/se7722.h>
-#elif defined(CONFIG_SH_7721_SOLUTION_ENGINE)
-#include <mach-se/mach/se7721.h>
-#endif
-
-/*
- * SolutionEngine Seriese
- *
- * about MS770xSE
- * 0xB8400000 : Common Memory
- * 0xB8500000 : Attribute
- * 0xB8600000 : I/O
- *
- * about MS7722SE
- * 0xB0400000 : Common Memory
- * 0xB0500000 : Attribute
- * 0xB0600000 : I/O
- */
-
-#if defined(CONFIG_SH_SOLUTION_ENGINE) || \
-    defined(CONFIG_SH_7722_SOLUTION_ENGINE) || \
-    defined(CONFIG_SH_7721_SOLUTION_ENGINE)
-static int __init cf_init_se(void)
-{
-       if ((ctrl_inw(MRSHPC_CSR) & 0x000c) != 0)
-               return 0;       /* Not detected */
-
-       if ((ctrl_inw(MRSHPC_CSR) & 0x0080) == 0) {
-               ctrl_outw(0x0674, MRSHPC_CPWCR); /* Card Vcc is 3.3v? */
-       } else {
-               ctrl_outw(0x0678, MRSHPC_CPWCR); /* Card Vcc is 5V */
-       }
-
-       /*
-        *  PC-Card window open
-        *  flag == COMMON/ATTRIBUTE/IO
-        */
-       /* common window open */
-       ctrl_outw(0x8a84, MRSHPC_MW0CR1);
-       if((ctrl_inw(MRSHPC_CSR) & 0x4000) != 0)
-               /* common mode & bus width 16bit SWAP = 1*/
-               ctrl_outw(0x0b00, MRSHPC_MW0CR2);
-       else
-               /* common mode & bus width 16bit SWAP = 0*/
-               ctrl_outw(0x0300, MRSHPC_MW0CR2);
-
-       /* attribute window open */
-       ctrl_outw(0x8a85, MRSHPC_MW1CR1);
-       if ((ctrl_inw(MRSHPC_CSR) & 0x4000) != 0)
-               /* attribute mode & bus width 16bit SWAP = 1*/
-               ctrl_outw(0x0a00, MRSHPC_MW1CR2);
-       else
-               /* attribute mode & bus width 16bit SWAP = 0*/
-               ctrl_outw(0x0200, MRSHPC_MW1CR2);
-
-       /* I/O window open */
-       ctrl_outw(0x8a86, MRSHPC_IOWCR1);
-       ctrl_outw(0x0008, MRSHPC_CDCR);  /* I/O card mode */
-       if ((ctrl_inw(MRSHPC_CSR) & 0x4000) != 0)
-               ctrl_outw(0x0a00, MRSHPC_IOWCR2); /* bus width 16bit SWAP = 1*/
-       else
-               ctrl_outw(0x0200, MRSHPC_IOWCR2); /* bus width 16bit SWAP = 0*/
-
-       ctrl_outw(0x2000, MRSHPC_ICR);
-       ctrl_outb(0x00, PA_MRSHPC_MW2 + 0x206);
-       ctrl_outb(0x42, PA_MRSHPC_MW2 + 0x200);
-       return 0;
-}
-#else
-static int __init cf_init_se(void)
-{
-       return -1;
-}
-#endif
-
-static int __init cf_init(void)
-{
-       if (mach_is_se() || mach_is_7722se() || mach_is_7721se())
-               return cf_init_se();
-
-       return cf_init_default();
-}
-
-__initcall (cf_init);
index b7e46d5..7b17137 100644 (file)
@@ -117,6 +117,11 @@ int clk_enable(struct clk *clk)
        unsigned long flags;
        int ret;
 
+       if (!clk)
+               return -EINVAL;
+
+       clk_enable(clk->parent);
+
        spin_lock_irqsave(&clock_lock, flags);
        ret = __clk_enable(clk);
        spin_unlock_irqrestore(&clock_lock, flags);
@@ -147,9 +152,14 @@ void clk_disable(struct clk *clk)
 {
        unsigned long flags;
 
+       if (!clk)
+               return;
+
        spin_lock_irqsave(&clock_lock, flags);
        __clk_disable(clk);
        spin_unlock_irqrestore(&clock_lock, flags);
+
+       clk_disable(clk->parent);
 }
 EXPORT_SYMBOL_GPL(clk_disable);
 
index 75fb03d..d29e69c 100644 (file)
@@ -261,9 +261,11 @@ asmlinkage void __init sh_cpu_init(void)
        cache_init();
 
        if (raw_smp_processor_id() == 0) {
+#ifdef CONFIG_MMU
                shm_align_mask = max_t(unsigned long,
                                       current_cpu_data.dcache.way_size - 1,
                                       PAGE_SIZE - 1);
+#endif
 
                /* Boot CPU sets the cache shape */
                detect_cache_shape();
index 428450c..45f85c7 100644 (file)
@@ -8,9 +8,10 @@ common-y       += ex.o entry.o
 
 obj-$(CONFIG_SH_FPU)   += fpu.o
 
-obj-$(CONFIG_CPU_SUBTYPE_SH7206)       += setup-sh7206.o clock-sh7206.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7201)       += setup-sh7201.o clock-sh7201.o
 obj-$(CONFIG_CPU_SUBTYPE_SH7203)       += setup-sh7203.o clock-sh7203.o
 obj-$(CONFIG_CPU_SUBTYPE_SH7263)       += setup-sh7203.o clock-sh7203.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7206)       += setup-sh7206.o clock-sh7206.o
 obj-$(CONFIG_CPU_SUBTYPE_MXG)          += setup-mxg.o clock-sh7206.o
 
 # Pinmux setup
diff --git a/arch/sh/kernel/cpu/sh2a/clock-sh7201.c b/arch/sh/kernel/cpu/sh2a/clock-sh7201.c
new file mode 100644 (file)
index 0000000..020a96f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * arch/sh/kernel/cpu/sh2a/clock-sh7201.c
+ *
+ * SH7201 support for the clock framework
+ *
+ *  Copyright (C) 2008 Peter Griffin  <pgriffin@mpc-data.co.uk>
+ *
+ * Based on clock-sh4.c
+ *  Copyright (C) 2005  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/clock.h>
+#include <asm/freq.h>
+#include <asm/io.h>
+
+const static int pll1rate[]={1,2,3,4,6,8};
+const static int pfc_divisors[]={1,2,3,4,6,8,12};
+#define ifc_divisors pfc_divisors
+
+#if (CONFIG_SH_CLK_MD == 0)
+#define PLL2 (4)
+#elif (CONFIG_SH_CLK_MD == 2)
+#define PLL2 (2)
+#elif (CONFIG_SH_CLK_MD == 3)
+#define PLL2 (1)
+#else
+#error "Illegal Clock Mode!"
+#endif
+
+static void master_clk_init(struct clk *clk)
+{
+       clk->rate = 10000000 * PLL2 * pll1rate[(ctrl_inw(FREQCR) >> 8) & 0x0007];
+}
+
+static struct clk_ops sh7201_master_clk_ops = {
+       .init           = master_clk_init,
+};
+
+static void module_clk_recalc(struct clk *clk)
+{
+       int idx = (ctrl_inw(FREQCR) & 0x0007);
+       clk->rate = clk->parent->rate / pfc_divisors[idx];
+}
+
+static struct clk_ops sh7201_module_clk_ops = {
+       .recalc         = module_clk_recalc,
+};
+
+static void bus_clk_recalc(struct clk *clk)
+{
+       int idx = (ctrl_inw(FREQCR) & 0x0007);
+       clk->rate = clk->parent->rate / pfc_divisors[idx];
+}
+
+static struct clk_ops sh7201_bus_clk_ops = {
+       .recalc         = bus_clk_recalc,
+};
+
+static void cpu_clk_recalc(struct clk *clk)
+{
+       int idx = ((ctrl_inw(FREQCR) >> 4) & 0x0007);
+       clk->rate = clk->parent->rate / ifc_divisors[idx];
+}
+
+static struct clk_ops sh7201_cpu_clk_ops = {
+       .recalc         = cpu_clk_recalc,
+};
+
+static struct clk_ops *sh7201_clk_ops[] = {
+       &sh7201_master_clk_ops,
+       &sh7201_module_clk_ops,
+       &sh7201_bus_clk_ops,
+       &sh7201_cpu_clk_ops,
+};
+
+void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
+{
+       if (idx < ARRAY_SIZE(sh7201_clk_ops))
+               *ops = sh7201_clk_ops[idx];
+}
index 6e79132..e098e2f 100644 (file)
@@ -18,16 +18,17 @@ int __init detect_cpu_and_cache_system(void)
        /* All SH-2A CPUs have support for 16 and 32-bit opcodes.. */
        boot_cpu_data.flags                     |= CPU_HAS_OP32;
 
-#if defined(CONFIG_CPU_SUBTYPE_SH7203)
+#if defined(CONFIG_CPU_SUBTYPE_SH7201)
+       boot_cpu_data.type                      = CPU_SH7201;
+       boot_cpu_data.flags                     |= CPU_HAS_FPU;
+#elif defined(CONFIG_CPU_SUBTYPE_SH7203)
        boot_cpu_data.type                      = CPU_SH7203;
-       /* SH7203 has an FPU.. */
        boot_cpu_data.flags                     |= CPU_HAS_FPU;
 #elif defined(CONFIG_CPU_SUBTYPE_SH7263)
        boot_cpu_data.type                      = CPU_SH7263;
        boot_cpu_data.flags                     |= CPU_HAS_FPU;
 #elif defined(CONFIG_CPU_SUBTYPE_SH7206)
        boot_cpu_data.type                      = CPU_SH7206;
-       /* While SH7206 has a DSP.. */
        boot_cpu_data.flags                     |= CPU_HAS_DSP;
 #elif defined(CONFIG_CPU_SUBTYPE_MXG)
        boot_cpu_data.type                      = CPU_MXG;
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7201.c b/arch/sh/kernel/cpu/sh2a/setup-sh7201.c
new file mode 100644 (file)
index 0000000..0631e42
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ *  SH7201 setup
+ *
+ *  Copyright (C) 2008  Peter Griffin pgriffin@mpc-data.co.uk
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/serial_sci.h>
+
+enum {
+       UNUSED = 0,
+
+       /* interrupt sources */
+       IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7,
+       PINT0, PINT1, PINT2, PINT3, PINT4, PINT5, PINT6, PINT7,
+       ADC_ADI,
+       MTU2_TGI0A, MTU2_TGI0B, MTU2_TGI0C, MTU2_TGI0D,
+       MTU2_TCI0V, MTU2_TGI0E, MTU2_TGI0F,
+       MTU2_TGI1A, MTU2_TGI1B, MTU2_TCI1V, MTU2_TCI1U,
+       MTU2_TGI2A, MTU2_TGI2B, MTU2_TCI2V, MTU2_TCI2U,
+       MTU2_TGI3A, MTU2_TGI3B, MTU2_TGI3C, MTU2_TGI3D, MTU2_TCI3V,
+       MTU2_TGI4A, MTU2_TGI4B, MTU2_TGI4C, MTU2_TGI4D, MTU2_TCI4V,
+       MTU2_TGI5U, MTU2_TGI5V, MTU2_TGI5W,
+       RTC_ARM, RTC_PRD, RTC_CUP,
+       WDT,
+       IIC30_STPI, IIC30_NAKI, IIC30_RXI, IIC30_TXI, IIC30_TEI,
+       IIC31_STPI, IIC31_NAKI, IIC31_RXI, IIC31_TXI, IIC31_TEI,
+       IIC32_STPI, IIC32_NAKI, IIC32_RXI, IIC32_TXI, IIC32_TEI,
+
+       DMAC0_DMINT0, DMAC1_DMINT1,
+       DMAC2_DMINT2, DMAC3_DMINT3,
+
+       SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI,
+       SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI,
+       SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI,
+       SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI,
+       SCIF4_BRI, SCIF4_ERI, SCIF4_RXI, SCIF4_TXI,
+       SCIF5_BRI, SCIF5_ERI, SCIF5_RXI, SCIF5_TXI,
+       SCIF6_BRI, SCIF6_ERI, SCIF6_RXI, SCIF6_TXI,
+       SCIF7_BRI, SCIF7_ERI, SCIF7_RXI, SCIF7_TXI,
+
+       DMAC0_DMINTA, DMAC4_DMINT4, DMAC5_DMINT5, DMAC6_DMINT6,
+       DMAC7_DMINT7,
+
+       RCAN0_ERS, RCAN0_OVR,
+       RCAN0_SLE,
+       RCAN0_RM0, RCAN0_RM1,
+
+       RCAN1_ERS, RCAN1_OVR,
+       RCAN1_SLE,
+       RCAN1_RM0, RCAN1_RM1,
+
+       SSI0_SSII, SSI1_SSII,
+
+       TMR0_CMIA0, TMR0_CMIB0, TMR0_OVI0,
+       TMR1_CMIA1, TMR1_CMIB1, TMR1_OVI1,
+
+       /* interrupt groups */
+
+       IRQ, PINT, ADC,
+       MTU20_ABCD, MTU20_VEF, MTU21_AB, MTU21_VU, MTU22_AB, MTU22_VU,
+       MTU23_ABCD, MTU24_ABCD, MTU25_UVW,
+       RTC, IIC30, IIC31, IIC32,
+       SCIF0, SCIF1, SCIF2, SCIF3, SCIF4, SCIF5, SCIF6, SCIF7,
+       RCAN0, RCAN1, TMR0, TMR1
+
+};
+
+static struct intc_vect vectors[] __initdata = {
+       INTC_IRQ(IRQ0, 64), INTC_IRQ(IRQ1, 65),
+       INTC_IRQ(IRQ2, 66), INTC_IRQ(IRQ3, 67),
+       INTC_IRQ(IRQ4, 68), INTC_IRQ(IRQ5, 69),
+       INTC_IRQ(IRQ6, 70), INTC_IRQ(IRQ7, 71),
+       INTC_IRQ(PINT0, 80), INTC_IRQ(PINT1, 81),
+       INTC_IRQ(PINT2, 82), INTC_IRQ(PINT3, 83),
+       INTC_IRQ(PINT4, 84), INTC_IRQ(PINT5, 85),
+       INTC_IRQ(PINT6, 86), INTC_IRQ(PINT7, 87),
+
+       INTC_IRQ(ADC_ADI, 92),
+
+       INTC_IRQ(MTU2_TGI0A, 108), INTC_IRQ(MTU2_TGI0B, 109),
+       INTC_IRQ(MTU2_TGI0C, 110), INTC_IRQ(MTU2_TGI0D, 111),
+       INTC_IRQ(MTU2_TCI0V, 112),
+       INTC_IRQ(MTU2_TGI0E, 113), INTC_IRQ(MTU2_TGI0F, 114),
+
+       INTC_IRQ(MTU2_TGI1A, 116), INTC_IRQ(MTU2_TGI1B, 117),
+       INTC_IRQ(MTU2_TCI1V, 120), INTC_IRQ(MTU2_TCI1U, 121),
+
+       INTC_IRQ(MTU2_TGI2A, 124), INTC_IRQ(MTU2_TGI2B, 125),
+       INTC_IRQ(MTU2_TCI2V, 128), INTC_IRQ(MTU2_TCI2U, 129),
+
+       INTC_IRQ(MTU2_TGI3A, 132), INTC_IRQ(MTU2_TGI3B, 133),
+       INTC_IRQ(MTU2_TGI3C, 134), INTC_IRQ(MTU2_TGI3D, 135),
+       INTC_IRQ(MTU2_TCI3V, 136),
+
+       INTC_IRQ(MTU2_TGI4A, 140), INTC_IRQ(MTU2_TGI4B, 141),
+       INTC_IRQ(MTU2_TGI4C, 142), INTC_IRQ(MTU2_TGI4D, 143),
+       INTC_IRQ(MTU2_TCI4V, 144),
+
+       INTC_IRQ(MTU2_TGI5U, 148), INTC_IRQ(MTU2_TGI5V, 149),
+       INTC_IRQ(MTU2_TGI5W, 150),
+
+       INTC_IRQ(RTC_ARM, 152), INTC_IRQ(RTC_PRD, 153),
+       INTC_IRQ(RTC_CUP, 154), INTC_IRQ(WDT, 156),
+
+       INTC_IRQ(IIC30_STPI, 157), INTC_IRQ(IIC30_NAKI, 158),
+       INTC_IRQ(IIC30_RXI, 159), INTC_IRQ(IIC30_TXI, 160),
+       INTC_IRQ(IIC30_TEI, 161),
+
+       INTC_IRQ(IIC31_STPI, 164), INTC_IRQ(IIC31_NAKI, 165),
+       INTC_IRQ(IIC31_RXI, 166), INTC_IRQ(IIC31_TXI, 167),
+       INTC_IRQ(IIC31_TEI, 168),
+
+       INTC_IRQ(IIC32_STPI, 170), INTC_IRQ(IIC32_NAKI, 171),
+       INTC_IRQ(IIC32_RXI, 172), INTC_IRQ(IIC32_TXI, 173),
+       INTC_IRQ(IIC32_TEI, 174),
+
+       INTC_IRQ(DMAC0_DMINT0, 176), INTC_IRQ(DMAC1_DMINT1, 177),
+       INTC_IRQ(DMAC2_DMINT2, 178), INTC_IRQ(DMAC3_DMINT3, 179),
+
+       INTC_IRQ(SCIF0_BRI, 180), INTC_IRQ(SCIF0_ERI, 181),
+       INTC_IRQ(SCIF0_RXI, 182), INTC_IRQ(SCIF0_TXI, 183),
+       INTC_IRQ(SCIF1_BRI, 184), INTC_IRQ(SCIF1_ERI, 185),
+       INTC_IRQ(SCIF1_RXI, 186), INTC_IRQ(SCIF1_TXI, 187),
+       INTC_IRQ(SCIF2_BRI, 188), INTC_IRQ(SCIF2_ERI, 189),
+       INTC_IRQ(SCIF2_RXI, 190), INTC_IRQ(SCIF2_TXI, 191),
+       INTC_IRQ(SCIF3_BRI, 192), INTC_IRQ(SCIF3_ERI, 193),
+       INTC_IRQ(SCIF3_RXI, 194), INTC_IRQ(SCIF3_TXI, 195),
+       INTC_IRQ(SCIF4_BRI, 196), INTC_IRQ(SCIF4_ERI, 197),
+       INTC_IRQ(SCIF4_RXI, 198), INTC_IRQ(SCIF4_TXI, 199),
+       INTC_IRQ(SCIF5_BRI, 200), INTC_IRQ(SCIF5_ERI, 201),
+       INTC_IRQ(SCIF5_RXI, 202), INTC_IRQ(SCIF5_TXI, 203),
+       INTC_IRQ(SCIF6_BRI, 204), INTC_IRQ(SCIF6_ERI, 205),
+       INTC_IRQ(SCIF6_RXI, 206), INTC_IRQ(SCIF6_TXI, 207),
+       INTC_IRQ(SCIF7_BRI, 208), INTC_IRQ(SCIF7_ERI, 209),
+       INTC_IRQ(SCIF7_RXI, 210), INTC_IRQ(SCIF7_TXI, 211),
+
+       INTC_IRQ(DMAC0_DMINTA, 212), INTC_IRQ(DMAC4_DMINT4, 216),
+       INTC_IRQ(DMAC5_DMINT5, 217), INTC_IRQ(DMAC6_DMINT6, 218),
+       INTC_IRQ(DMAC7_DMINT7, 219),
+
+       INTC_IRQ(RCAN0_ERS, 228), INTC_IRQ(RCAN0_OVR, 229),
+       INTC_IRQ(RCAN0_SLE, 230),
+       INTC_IRQ(RCAN0_RM0, 231), INTC_IRQ(RCAN0_RM1, 232),
+
+       INTC_IRQ(RCAN1_ERS, 234), INTC_IRQ(RCAN1_OVR, 235),
+       INTC_IRQ(RCAN1_SLE, 236),
+       INTC_IRQ(RCAN1_RM0, 237), INTC_IRQ(RCAN1_RM1, 238),
+
+       INTC_IRQ(SSI0_SSII, 244), INTC_IRQ(SSI1_SSII, 245),
+
+       INTC_IRQ(TMR0_CMIA0, 246), INTC_IRQ(TMR0_CMIB0, 247),
+       INTC_IRQ(TMR0_OVI0, 248),
+
+       INTC_IRQ(TMR1_CMIA1, 252), INTC_IRQ(TMR1_CMIB1, 253),
+       INTC_IRQ(TMR1_OVI1, 254),
+
+};
+
+static struct intc_group groups[] __initdata = {
+       INTC_GROUP(PINT, PINT0, PINT1, PINT2, PINT3,
+                  PINT4, PINT5, PINT6, PINT7),
+       INTC_GROUP(MTU20_ABCD, MTU2_TGI0A, MTU2_TGI0B, MTU2_TGI0C, MTU2_TGI0D),
+       INTC_GROUP(MTU20_VEF, MTU2_TCI0V, MTU2_TGI0E, MTU2_TGI0F),
+
+       INTC_GROUP(MTU21_AB, MTU2_TGI1A, MTU2_TGI1B),
+       INTC_GROUP(MTU21_VU, MTU2_TCI1V, MTU2_TCI1U),
+       INTC_GROUP(MTU22_AB, MTU2_TGI2A, MTU2_TGI2B),
+       INTC_GROUP(MTU22_VU, MTU2_TCI2V, MTU2_TCI2U),
+       INTC_GROUP(MTU23_ABCD, MTU2_TGI3A, MTU2_TGI3B, MTU2_TGI3C, MTU2_TGI3D),
+       INTC_GROUP(MTU24_ABCD, MTU2_TGI4A, MTU2_TGI4B, MTU2_TGI4C, MTU2_TGI4D),
+       INTC_GROUP(MTU25_UVW, MTU2_TGI5U, MTU2_TGI5V, MTU2_TGI5W),
+       INTC_GROUP(RTC, RTC_ARM, RTC_PRD, RTC_CUP ),
+
+       INTC_GROUP(IIC30, IIC30_STPI, IIC30_NAKI, IIC30_RXI, IIC30_TXI,
+                  IIC30_TEI),
+       INTC_GROUP(IIC31, IIC31_STPI, IIC31_NAKI, IIC31_RXI, IIC31_TXI,
+                  IIC31_TEI),
+       INTC_GROUP(IIC32, IIC32_STPI, IIC32_NAKI, IIC32_RXI, IIC32_TXI,
+                  IIC32_TEI),
+
+       INTC_GROUP(SCIF0, SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI),
+       INTC_GROUP(SCIF1, SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI),
+       INTC_GROUP(SCIF2, SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI),
+       INTC_GROUP(SCIF3, SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI),
+       INTC_GROUP(SCIF4, SCIF4_BRI, SCIF4_ERI, SCIF4_RXI, SCIF4_TXI),
+       INTC_GROUP(SCIF5, SCIF5_BRI, SCIF5_ERI, SCIF5_RXI, SCIF5_TXI),
+       INTC_GROUP(SCIF6, SCIF6_BRI, SCIF6_ERI, SCIF6_RXI, SCIF6_TXI),
+       INTC_GROUP(SCIF7, SCIF7_BRI, SCIF7_ERI, SCIF7_RXI, SCIF7_TXI),
+
+       INTC_GROUP(RCAN0, RCAN0_ERS, RCAN0_OVR, RCAN0_RM0, RCAN0_RM1,
+                  RCAN0_SLE),
+       INTC_GROUP(RCAN1, RCAN1_ERS, RCAN1_OVR, RCAN1_RM0, RCAN1_RM1,
+                  RCAN1_SLE),
+
+       INTC_GROUP(TMR0, TMR0_CMIA0, TMR0_CMIB0, TMR0_OVI0),
+       INTC_GROUP(TMR1, TMR1_CMIA1, TMR1_CMIB1, TMR1_OVI1),
+};
+
+static struct intc_prio_reg prio_registers[] __initdata = {
+       { 0xfffe9418, 0, 16, 4, /* IPR01 */ { IRQ0, IRQ1, IRQ2, IRQ3 } },
+       { 0xfffe941a, 0, 16, 4, /* IPR02 */ { IRQ4, IRQ5, IRQ6, IRQ7 } },
+       { 0xfffe9420, 0, 16, 4, /* IPR05 */ { PINT, 0, ADC_ADI, 0 } },
+       { 0xfffe9800, 0, 16, 4, /* IPR06 */ { 0, MTU20_ABCD, MTU20_VEF, MTU21_AB } },
+       { 0xfffe9802, 0, 16, 4, /* IPR07 */ { MTU21_VU, MTU22_AB, MTU22_VU,  MTU23_ABCD } },
+       { 0xfffe9804, 0, 16, 4, /* IPR08 */ { MTU2_TCI3V, MTU24_ABCD, MTU2_TCI4V, MTU25_UVW } },
+
+       { 0xfffe9806, 0, 16, 4, /* IPR09 */ { RTC, WDT, IIC30, 0 } },
+       { 0xfffe9808, 0, 16, 4, /* IPR10 */ { IIC31, IIC32, DMAC0_DMINT0, DMAC1_DMINT1 } },
+       { 0xfffe980a, 0, 16, 4, /* IPR11 */ { DMAC2_DMINT2, DMAC3_DMINT3, SCIF0 , SCIF1 } },
+       { 0xfffe980c, 0, 16, 4, /* IPR12 */ { SCIF2, SCIF3, SCIF4, SCIF5 } },
+       { 0xfffe980e, 0, 16, 4, /* IPR13 */ { SCIF6, SCIF7, DMAC0_DMINTA, DMAC4_DMINT4  } },
+       { 0xfffe9810, 0, 16, 4, /* IPR14 */ { DMAC5_DMINT5, DMAC6_DMINT6, DMAC7_DMINT7, 0 } },
+       { 0xfffe9812, 0, 16, 4, /* IPR15 */ { 0, RCAN0, RCAN1, 0 } },
+       { 0xfffe9814, 0, 16, 4, /* IPR16 */ { SSI0_SSII, SSI1_SSII, TMR0, TMR1 } },
+};
+
+static struct intc_mask_reg mask_registers[] __initdata = {
+       { 0xfffe9408, 0, 16, /* PINTER */
+         { 0, 0, 0, 0, 0, 0, 0, 0,
+           PINT7, PINT6, PINT5, PINT4, PINT3, PINT2, PINT1, PINT0 } },
+};
+
+static DECLARE_INTC_DESC(intc_desc, "sh7201", vectors, groups,
+                        mask_registers, prio_registers, NULL);
+
+static struct plat_sci_port sci_platform_data[] = {
+       {
+               .mapbase        = 0xfffe8000,
+               .flags          = UPF_BOOT_AUTOCONF,
+               .type           = PORT_SCIF,
+               .irqs           = { 181, 182, 183, 180}
+       }, {
+               .mapbase        = 0xfffe8800,
+               .flags          = UPF_BOOT_AUTOCONF,
+               .type           = PORT_SCIF,
+               .irqs           = { 185, 186, 187, 184}
+       }, {
+               .mapbase        = 0xfffe9000,
+               .flags          = UPF_BOOT_AUTOCONF,
+               .type           = PORT_SCIF,
+               .irqs           = { 189, 186, 187, 188}
+       }, {
+               .mapbase        = 0xfffe9800,
+               .flags          = UPF_BOOT_AUTOCONF,
+               .type           = PORT_SCIF,
+               .irqs           = { 193, 194, 195, 192}
+       }, {
+               .mapbase        = 0xfffea000,
+               .flags          = UPF_BOOT_AUTOCONF,
+               .type           = PORT_SCIF,
+               .irqs           = { 196, 198, 199, 196}
+       }, {
+               .mapbase        = 0xfffea800,
+               .flags          = UPF_BOOT_AUTOCONF,
+               .type           = PORT_SCIF,
+               .irqs           = { 201, 202, 203, 200}
+       }, {
+               .mapbase        = 0xfffeb000,
+               .flags          = UPF_BOOT_AUTOCONF,
+               .type           = PORT_SCIF,
+               .irqs           = { 205, 206, 207, 204}
+       }, {
+               .mapbase        = 0xfffeb800,
+               .flags          = UPF_BOOT_AUTOCONF,
+               .type           = PORT_SCIF,
+               .irqs           = { 209, 210, 211, 208}
+       }, {
+               .flags = 0,
+       }
+};
+
+static struct platform_device sci_device = {
+       .name           = "sh-sci",
+       .id             = -1,
+       .dev            = {
+               .platform_data  = sci_platform_data,
+       },
+};
+
+static struct resource rtc_resources[] = {
+       [0] = {
+               .start  = 0xffff0800,
+               .end    = 0xffff2000 + 0x58 - 1,
+               .flags  = IORESOURCE_IO,
+       },
+       [1] = {
+               /* Period IRQ */
+               .start  = 153,
+               .flags  = IORESOURCE_IRQ,
+       },
+       [2] = {
+               /* Carry IRQ */
+               .start  = 154,
+               .flags  = IORESOURCE_IRQ,
+       },
+       [3] = {
+               /* Alarm IRQ */
+               .start  = 152,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device rtc_device = {
+       .name           = "sh-rtc",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(rtc_resources),
+       .resource       = rtc_resources,
+};
+
+static struct platform_device *sh7201_devices[] __initdata = {
+       &sci_device,
+       &rtc_device,
+};
+
+static int __init sh7201_devices_setup(void)
+{
+       return platform_add_devices(sh7201_devices,
+                                   ARRAY_SIZE(sh7201_devices));
+}
+__initcall(sh7201_devices_setup);
+
+void __init plat_irq_setup(void)
+{
+       register_intc_controller(&intc_desc);
+}
index 3fe482d..b4106d0 100644 (file)
@@ -52,7 +52,7 @@
  *     syscall #
  *
  */
-#if defined(CONFIG_KGDB_NMI)
+#if defined(CONFIG_KGDB)
 NMI_VEC = 0x1c0                        ! Must catch early for debounce
 #endif
 
@@ -307,7 +307,7 @@ skip_restore:
 6:     or      k0, k2                  ! Set the IMASK-bits
        ldc     k2, ssr
        !
-#if defined(CONFIG_KGDB_NMI)
+#if defined(CONFIG_KGDB)
        ! Clear in_nmi
        mov.l   6f, k0
        mov     #0, k1
@@ -320,7 +320,7 @@ skip_restore:
 
        .align  2
 5:     .long   0x00001000      ! DSP
-#ifdef CONFIG_KGDB_NMI
+#ifdef CONFIG_KGDB
 6:     .long   in_nmi
 #endif
 7:     .long   0x30000000
@@ -376,9 +376,9 @@ tlb_miss:
 !
        .balign         512,0,512
 interrupt:
-       mov.l   2f, k2
        mov.l   3f, k3
-#if defined(CONFIG_KGDB_NMI)
+#if defined(CONFIG_KGDB)
+       mov.l   2f, k2
        ! Debounce (filter nested NMI)
        mov.l   @k2, k0
        mov.l   5f, k1
@@ -390,16 +390,16 @@ interrupt:
        rte
         nop
        .align  2
+2:     .long   INTEVT
 5:     .long   NMI_VEC
 6:     .long   in_nmi
 0:
-#endif /* defined(CONFIG_KGDB_NMI) */
+#endif /* defined(CONFIG_KGDB) */
        bra     handle_exception
         mov    #-1, k2         ! interrupt exception marker
 
        .align  2
 1:     .long   EXPEVT
-2:     .long   INTEVT
 3:     .long   ret_from_irq
 4:     .long   ret_from_exception
 
index dac4297..e5a0de3 100644 (file)
@@ -26,7 +26,7 @@
 #define        fpu_error_trap_handler          exception_error
 #endif
 
-#if !defined(CONFIG_KGDB_NMI)
+#if !defined(CONFIG_KGDB)
 #define kgdb_handle_exception          exception_error
 #endif
 
index 2b747f3..42edf2e 100644 (file)
@@ -37,6 +37,7 @@
  */
 #include <linux/kernel.h>
 #include <cpu/fpu.h>
+#include <asm/div64.h>
 
 #define LIT64( a ) a##LL
 
@@ -67,16 +68,16 @@ typedef unsigned long long float64;
 extern void float_raise(unsigned int flags);   /* in fpu.c */
 extern int float_rounding_mode(void);  /* in fpu.c */
 
-inline bits64 extractFloat64Frac(float64 a);
-inline flag extractFloat64Sign(float64 a);
-inline int16 extractFloat64Exp(float64 a);
-inline int16 extractFloat32Exp(float32 a);
-inline flag extractFloat32Sign(float32 a);
-inline bits32 extractFloat32Frac(float32 a);
-inline float64 packFloat64(flag zSign, int16 zExp, bits64 zSig);
-inline void shift64RightJamming(bits64 a, int16 count, bits64 * zPtr);
-inline float32 packFloat32(flag zSign, int16 zExp, bits32 zSig);
-inline void shift32RightJamming(bits32 a, int16 count, bits32 * zPtr);
+bits64 extractFloat64Frac(float64 a);
+flag extractFloat64Sign(float64 a);
+int16 extractFloat64Exp(float64 a);
+int16 extractFloat32Exp(float32 a);
+flag extractFloat32Sign(float32 a);
+bits32 extractFloat32Frac(float32 a);
+float64 packFloat64(flag zSign, int16 zExp, bits64 zSig);
+void shift64RightJamming(bits64 a, int16 count, bits64 * zPtr);
+float32 packFloat32(flag zSign, int16 zExp, bits32 zSig);
+void shift32RightJamming(bits32 a, int16 count, bits32 * zPtr);
 float64 float64_sub(float64 a, float64 b);
 float32 float32_sub(float32 a, float32 b);
 float32 float32_add(float32 a, float32 b);
@@ -86,11 +87,11 @@ float32 float32_div(float32 a, float32 b);
 float32 float32_mul(float32 a, float32 b);
 float64 float64_mul(float64 a, float64 b);
 float32 float64_to_float32(float64 a);
-inline void add128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,
+void add128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,
                   bits64 * z1Ptr);
-inline void sub128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,
+void sub128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,
                   bits64 * z1Ptr);
-inline void mul64To128(bits64 a, bits64 b, bits64 * z0Ptr, bits64 * z1Ptr);
+void mul64To128(bits64 a, bits64 b, bits64 * z0Ptr, bits64 * z1Ptr);
 
 static int8 countLeadingZeros32(bits32 a);
 static int8 countLeadingZeros64(bits64 a);
@@ -110,42 +111,42 @@ static bits64 estimateDiv128To64(bits64 a0, bits64 a1, bits64 b);
 static void normalizeFloat32Subnormal(bits32 aSig, int16 * zExpPtr,
                                      bits32 * zSigPtr);
 
-inline bits64 extractFloat64Frac(float64 a)
+bits64 extractFloat64Frac(float64 a)
 {
        return a & LIT64(0x000FFFFFFFFFFFFF);
 }
 
-inline flag extractFloat64Sign(float64 a)
+flag extractFloat64Sign(float64 a)
 {
        return a >> 63;
 }
 
-inline int16 extractFloat64Exp(float64 a)
+int16 extractFloat64Exp(float64 a)
 {
        return (a >> 52) & 0x7FF;
 }
 
-inline int16 extractFloat32Exp(float32 a)
+int16 extractFloat32Exp(float32 a)
 {
        return (a >> 23) & 0xFF;
 }
 
-inline flag extractFloat32Sign(float32 a)
+flag extractFloat32Sign(float32 a)
 {
        return a >> 31;
 }
 
-inline bits32 extractFloat32Frac(float32 a)
+bits32 extractFloat32Frac(float32 a)
 {
        return a & 0x007FFFFF;
 }
 
-inline float64 packFloat64(flag zSign, int16 zExp, bits64 zSig)
+float64 packFloat64(flag zSign, int16 zExp, bits64 zSig)
 {
        return (((bits64) zSign) << 63) + (((bits64) zExp) << 52) + zSig;
 }
 
-inline void shift64RightJamming(bits64 a, int16 count, bits64 * zPtr)
+void shift64RightJamming(bits64 a, int16 count, bits64 * zPtr)
 {
        bits64 z;
 
@@ -338,12 +339,12 @@ static float64 addFloat64Sigs(float64 a, float64 b, flag zSign)
 
 }
 
-inline float32 packFloat32(flag zSign, int16 zExp, bits32 zSig)
+float32 packFloat32(flag zSign, int16 zExp, bits32 zSig)
 {
        return (((bits32) zSign) << 31) + (((bits32) zExp) << 23) + zSig;
 }
 
-inline void shift32RightJamming(bits32 a, int16 count, bits32 * zPtr)
+void shift32RightJamming(bits32 a, int16 count, bits32 * zPtr)
 {
        bits32 z;
        if (count == 0) {
@@ -634,7 +635,7 @@ normalizeFloat64Subnormal(bits64 aSig, int16 * zExpPtr, bits64 * zSigPtr)
        *zExpPtr = 1 - shiftCount;
 }
 
-inline void add128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,
+void add128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,
                   bits64 * z1Ptr)
 {
        bits64 z1;
@@ -644,7 +645,7 @@ inline void add128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,
        *z0Ptr = a0 + b0 + (z1 < a1);
 }
 
-inline void
+void
 sub128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,
        bits64 * z1Ptr)
 {
@@ -656,11 +657,14 @@ static bits64 estimateDiv128To64(bits64 a0, bits64 a1, bits64 b)
 {
        bits64 b0, b1;
        bits64 rem0, rem1, term0, term1;
-       bits64 z;
+       bits64 z, tmp;
        if (b <= a0)
                return LIT64(0xFFFFFFFFFFFFFFFF);
        b0 = b >> 32;
-       z = (b0 << 32 <= a0) ? LIT64(0xFFFFFFFF00000000) : (a0 / b0) << 32;
+       tmp = a0;
+       do_div(tmp, b0);
+
+       z = (b0 << 32 <= a0) ? LIT64(0xFFFFFFFF00000000) : tmp << 32;
        mul64To128(b, z, &term0, &term1);
        sub128(a0, a1, term0, term1, &rem0, &rem1);
        while (((sbits64) rem0) < 0) {
@@ -669,11 +673,13 @@ static bits64 estimateDiv128To64(bits64 a0, bits64 a1, bits64 b)
                add128(rem0, rem1, b0, b1, &rem0, &rem1);
        }
        rem0 = (rem0 << 32) | (rem1 >> 32);
-       z |= (b0 << 32 <= rem0) ? 0xFFFFFFFF : rem0 / b0;
+       tmp = rem0;
+       do_div(tmp, b0);
+       z |= (b0 << 32 <= rem0) ? 0xFFFFFFFF : tmp;
        return z;
 }
 
-inline void mul64To128(bits64 a, bits64 b, bits64 * z0Ptr, bits64 * z1Ptr)
+void mul64To128(bits64 a, bits64 b, bits64 * z0Ptr, bits64 * z1Ptr)
 {
        bits32 aHigh, aLow, bHigh, bLow;
        bits64 z0, zMiddleA, zMiddleB, z1;
@@ -769,7 +775,8 @@ float32 float32_div(float32 a, float32 b)
 {
        flag aSign, bSign, zSign;
        int16 aExp, bExp, zExp;
-       bits32 aSig, bSig, zSig;
+       bits32 aSig, bSig;
+       uint64_t zSig;
 
        aSig = extractFloat32Frac(a);
        aExp = extractFloat32Exp(a);
@@ -804,11 +811,13 @@ float32 float32_div(float32 a, float32 b)
                aSig >>= 1;
                ++zExp;
        }
-       zSig = (((bits64) aSig) << 32) / bSig;
+       zSig = (((bits64) aSig) << 32);
+       do_div(zSig, bSig);
+
        if ((zSig & 0x3F) == 0) {
                zSig |= (((bits64) bSig) * zSig != ((bits64) aSig) << 32);
        }
-       return roundAndPackFloat32(zSign, zExp, zSig);
+       return roundAndPackFloat32(zSign, zExp, (bits32)zSig);
 
 }
 
index db91385..0e174af 100644 (file)
@@ -229,7 +229,7 @@ struct frqcr_context sh7722_get_clk_context(const char *name)
 }
 
 /**
- * sh7722_find_divisors - find divisor for setting rate
+ * sh7722_find_div_index - find divisor for setting rate
  *
  * All sh7722 clocks use the same set of multipliers/divisors. This function
  * chooses correct divisor to set the rate of clock with parent clock that
@@ -238,7 +238,7 @@ struct frqcr_context sh7722_get_clk_context(const char *name)
  * @parent_rate: rate of parent clock
  * @rate: requested rate to be set
  */
-static int sh7722_find_divisors(unsigned long parent_rate, unsigned rate)
+static int sh7722_find_div_index(unsigned long parent_rate, unsigned rate)
 {
        unsigned div2 = parent_rate * 2 / rate;
        int index;
@@ -247,12 +247,12 @@ static int sh7722_find_divisors(unsigned long parent_rate, unsigned rate)
                return -EINVAL;
 
        for (index = 1; index < ARRAY_SIZE(divisors2); index++) {
-               if (div2 > divisors2[index] && div2 <= divisors2[index])
+               if (div2 > divisors2[index - 1] && div2 <= divisors2[index])
                        break;
        }
        if (index >= ARRAY_SIZE(divisors2))
                index = ARRAY_SIZE(divisors2) - 1;
-       return divisors2[index];
+       return index;
 }
 
 static void sh7722_frqcr_recalc(struct clk *clk)
@@ -279,12 +279,12 @@ static int sh7722_frqcr_set_rate(struct clk *clk, unsigned long rate,
                return -EINVAL;
 
        /* look for multiplier/divisor pair */
-       div = sh7722_find_divisors(parent_rate, rate);
+       div = sh7722_find_div_index(parent_rate, rate);
        if (div<0)
                return div;
 
        /* calculate new value of clock rate */
-       clk->rate = parent_rate * 2 / div;
+       clk->rate = parent_rate * 2 / divisors2[div];
        frqcr = ctrl_inl(FRQCR);
 
        /* FIXME: adjust as algo_id specifies */
@@ -353,7 +353,7 @@ static int sh7722_frqcr_set_rate(struct clk *clk, unsigned long rate,
                        int part_div;
 
                        if (likely(!err)) {
-                               part_div = sh7722_find_divisors(parent_rate,
+                               part_div = sh7722_find_div_index(parent_rate,
                                                                rate);
                                if (part_div > 0) {
                                        part_ctx = sh7722_get_clk_context(
@@ -394,12 +394,12 @@ static long sh7722_frqcr_round_rate(struct clk *clk, unsigned long rate)
        int div;
 
        /* look for multiplier/divisor pair */
-       div = sh7722_find_divisors(parent_rate, rate);
+       div = sh7722_find_div_index(parent_rate, rate);
        if (div < 0)
                return clk->rate;
 
        /* calculate new value of clock rate */
-       return parent_rate * 2 / div;
+       return parent_rate * 2 / divisors2[div];
 }
 
 static struct clk_ops sh7722_frqcr_clk_ops = {
@@ -421,7 +421,7 @@ static int sh7722_siu_set_rate(struct clk *clk, unsigned long rate, int algo_id)
        int div;
 
        r = ctrl_inl(clk->arch_flags);
-       div = sh7722_find_divisors(clk->parent->rate, rate);
+       div = sh7722_find_div_index(clk->parent->rate, rate);
        if (div < 0)
                return div;
        r = (r & ~0xF) | div;
@@ -516,16 +516,19 @@ static struct clk_ops sh7722_video_clk_ops = {
 static struct clk sh7722_umem_clock = {
        .name = "umem_clk",
        .ops = &sh7722_frqcr_clk_ops,
+       .flags = CLK_RATE_PROPAGATES,
 };
 
 static struct clk sh7722_sh_clock = {
        .name = "sh_clk",
        .ops = &sh7722_frqcr_clk_ops,
+       .flags = CLK_RATE_PROPAGATES,
 };
 
 static struct clk sh7722_peripheral_clock = {
        .name = "peripheral_clk",
        .ops = &sh7722_frqcr_clk_ops,
+       .flags = CLK_RATE_PROPAGATES,
 };
 
 static struct clk sh7722_sdram_clock = {
@@ -533,6 +536,11 @@ static struct clk sh7722_sdram_clock = {
        .ops = &sh7722_frqcr_clk_ops,
 };
 
+static struct clk sh7722_r_clock = {
+       .name = "r_clk",
+       .rate = 32768,
+       .flags = CLK_RATE_PROPAGATES,
+};
 
 #ifndef CONFIG_CPU_SUBTYPE_SH7343
 
@@ -567,12 +575,30 @@ static struct clk sh7722_video_clock = {
        .ops = &sh7722_video_clk_ops,
 };
 
-static int sh7722_mstpcr_start_stop(struct clk *clk, unsigned long reg,
-                                   int enable)
+#define MSTPCR_ARCH_FLAGS(reg, bit) (((reg) << 8) | (bit))
+#define MSTPCR_ARCH_FLAGS_REG(value) ((value) >> 8)
+#define MSTPCR_ARCH_FLAGS_BIT(value) ((value) & 0xff)
+
+static int sh7722_mstpcr_start_stop(struct clk *clk, int enable)
 {
-       unsigned long bit = clk->arch_flags;
+       unsigned long bit = MSTPCR_ARCH_FLAGS_BIT(clk->arch_flags);
+       unsigned long reg;
        unsigned long r;
 
+       switch(MSTPCR_ARCH_FLAGS_REG(clk->arch_flags)) {
+       case 0:
+               reg = MSTPCR0;
+               break;
+       case 1:
+               reg = MSTPCR1;
+               break;
+       case 2:
+               reg = MSTPCR2;
+               break;
+       default:
+               return -EINVAL;
+       }  
+
        r = ctrl_inl(reg);
 
        if (enable)
@@ -584,96 +610,175 @@ static int sh7722_mstpcr_start_stop(struct clk *clk, unsigned long reg,
        return 0;
 }
 
-static void sh7722_mstpcr0_enable(struct clk *clk)
-{
-       sh7722_mstpcr_start_stop(clk, MSTPCR0, 1);
-}
-
-static void sh7722_mstpcr0_disable(struct clk *clk)
-{
-       sh7722_mstpcr_start_stop(clk, MSTPCR0, 0);
-}
-
-static void sh7722_mstpcr1_enable(struct clk *clk)
-{
-       sh7722_mstpcr_start_stop(clk, MSTPCR1, 1);
-}
-
-static void sh7722_mstpcr1_disable(struct clk *clk)
+static void sh7722_mstpcr_enable(struct clk *clk)
 {
-       sh7722_mstpcr_start_stop(clk, MSTPCR1, 0);
+       sh7722_mstpcr_start_stop(clk, 1);
 }
 
-static void sh7722_mstpcr2_enable(struct clk *clk)
+static void sh7722_mstpcr_disable(struct clk *clk)
 {
-       sh7722_mstpcr_start_stop(clk, MSTPCR2, 1);
+       sh7722_mstpcr_start_stop(clk, 0);
 }
 
-static void sh7722_mstpcr2_disable(struct clk *clk)
+static void sh7722_mstpcr_recalc(struct clk *clk)
 {
-       sh7722_mstpcr_start_stop(clk, MSTPCR2, 0);
+       if (clk->parent)
+               clk->rate = clk->parent->rate;
 }
 
-static struct clk_ops sh7722_mstpcr0_clk_ops = {
-       .enable = sh7722_mstpcr0_enable,
-       .disable = sh7722_mstpcr0_disable,
-};
-
-static struct clk_ops sh7722_mstpcr1_clk_ops = {
-       .enable = sh7722_mstpcr1_enable,
-       .disable = sh7722_mstpcr1_disable,
+static struct clk_ops sh7722_mstpcr_clk_ops = {
+       .enable = sh7722_mstpcr_enable,
+       .disable = sh7722_mstpcr_disable,
+       .recalc = sh7722_mstpcr_recalc,
 };
 
-static struct clk_ops sh7722_mstpcr2_clk_ops = {
-       .enable = sh7722_mstpcr2_enable,
-       .disable = sh7722_mstpcr2_disable,
-};
-
-#define DECLARE_MSTPCRN(regnr, bitnr, bitstr)          \
-{                                                      \
-       .name = "mstp" __stringify(regnr) bitstr,       \
-       .arch_flags = bitnr,                            \
-       .ops = &sh7722_mstpcr ## regnr ## _clk_ops,     \
+#define MSTPCR(_name, _parent, regnr, bitnr) \
+{                                              \
+       .name = _name,                          \
+       .arch_flags = MSTPCR_ARCH_FLAGS(regnr, bitnr),  \
+       .ops = (void *)_parent,         \
 }
 
-#define DECLARE_MSTPCR(regnr) \
-       DECLARE_MSTPCRN(regnr, 31, "31"), \
-       DECLARE_MSTPCRN(regnr, 30, "30"), \
-       DECLARE_MSTPCRN(regnr, 29, "29"), \
-       DECLARE_MSTPCRN(regnr, 28, "28"), \
-       DECLARE_MSTPCRN(regnr, 27, "27"), \
-       DECLARE_MSTPCRN(regnr, 26, "26"), \
-       DECLARE_MSTPCRN(regnr, 25, "25"), \
-       DECLARE_MSTPCRN(regnr, 24, "24"), \
-       DECLARE_MSTPCRN(regnr, 23, "23"), \
-       DECLARE_MSTPCRN(regnr, 22, "22"), \
-       DECLARE_MSTPCRN(regnr, 21, "21"), \
-       DECLARE_MSTPCRN(regnr, 20, "20"), \
-       DECLARE_MSTPCRN(regnr, 19, "19"), \
-       DECLARE_MSTPCRN(regnr, 18, "18"), \
-       DECLARE_MSTPCRN(regnr, 17, "17"), \
-       DECLARE_MSTPCRN(regnr, 16, "16"), \
-       DECLARE_MSTPCRN(regnr, 15, "15"), \
-       DECLARE_MSTPCRN(regnr, 14, "14"), \
-       DECLARE_MSTPCRN(regnr, 13, "13"), \
-       DECLARE_MSTPCRN(regnr, 12, "12"), \
-       DECLARE_MSTPCRN(regnr, 11, "11"), \
-       DECLARE_MSTPCRN(regnr, 10, "10"), \
-       DECLARE_MSTPCRN(regnr, 9, "09"), \
-       DECLARE_MSTPCRN(regnr, 8, "08"), \
-       DECLARE_MSTPCRN(regnr, 7, "07"), \
-       DECLARE_MSTPCRN(regnr, 6, "06"), \
-       DECLARE_MSTPCRN(regnr, 5, "05"), \
-       DECLARE_MSTPCRN(regnr, 4, "04"), \
-       DECLARE_MSTPCRN(regnr, 3, "03"), \
-       DECLARE_MSTPCRN(regnr, 2, "02"), \
-       DECLARE_MSTPCRN(regnr, 1, "01"), \
-       DECLARE_MSTPCRN(regnr, 0, "00")
-
-static struct clk sh7722_mstpcr[] = {
-       DECLARE_MSTPCR(0),
-       DECLARE_MSTPCR(1),
-       DECLARE_MSTPCR(2),
+static struct clk sh7722_mstpcr_clocks[] = {
+#if defined(CONFIG_CPU_SUBTYPE_SH7722)
+       MSTPCR("uram0", "umem_clk", 0, 28),
+       MSTPCR("xymem0", "bus_clk", 0, 26),
+       MSTPCR("tmu0", "peripheral_clk", 0, 15),
+       MSTPCR("cmt0", "r_clk", 0, 14),
+       MSTPCR("rwdt0", "r_clk", 0, 13),
+       MSTPCR("flctl0", "peripheral_clk", 0, 10),
+       MSTPCR("scif0", "peripheral_clk", 0, 7),
+       MSTPCR("scif1", "peripheral_clk", 0, 6),
+       MSTPCR("scif2", "peripheral_clk", 0, 5),
+       MSTPCR("i2c0", "peripheral_clk", 1, 9),
+       MSTPCR("rtc0", "r_clk", 1, 8),
+       MSTPCR("sdhi0", "peripheral_clk", 2, 18),
+       MSTPCR("keysc0", "r_clk", 2, 14),
+       MSTPCR("usbf0", "peripheral_clk", 2, 11),
+       MSTPCR("2dg0", "bus_clk", 2, 9),
+       MSTPCR("siu0", "bus_clk", 2, 8),
+       MSTPCR("vou0", "bus_clk", 2, 5),
+       MSTPCR("jpu0", "bus_clk", 2, 6),
+       MSTPCR("beu0", "bus_clk", 2, 4),
+       MSTPCR("ceu0", "bus_clk", 2, 3),
+       MSTPCR("veu0", "bus_clk", 2, 2),
+       MSTPCR("vpu0", "bus_clk", 2, 1),
+       MSTPCR("lcdc0", "bus_clk", 2, 0),
+#endif
+#if defined(CONFIG_CPU_SUBTYPE_SH7723)
+       /* See page 60 of Datasheet V1.0: Overview -> Block Diagram */
+       MSTPCR("tlb0", "cpu_clk", 0, 31),
+       MSTPCR("ic0", "cpu_clk", 0, 30),
+       MSTPCR("oc0", "cpu_clk", 0, 29),
+       MSTPCR("l2c0", "sh_clk", 0, 28),
+       MSTPCR("ilmem0", "cpu_clk", 0, 27),
+       MSTPCR("fpu0", "cpu_clk", 0, 24),
+       MSTPCR("intc0", "cpu_clk", 0, 22),
+       MSTPCR("dmac0", "bus_clk", 0, 21),
+       MSTPCR("sh0", "sh_clk", 0, 20),
+       MSTPCR("hudi0", "peripheral_clk", 0, 19),
+       MSTPCR("ubc0", "cpu_clk", 0, 17),
+       MSTPCR("tmu0", "peripheral_clk", 0, 15),
+       MSTPCR("cmt0", "r_clk", 0, 14),
+       MSTPCR("rwdt0", "r_clk", 0, 13),
+       MSTPCR("dmac1", "bus_clk", 0, 12),
+       MSTPCR("tmu1", "peripheral_clk", 0, 11),
+       MSTPCR("flctl0", "peripheral_clk", 0, 10),
+       MSTPCR("scif0", "peripheral_clk", 0, 9),
+       MSTPCR("scif1", "peripheral_clk", 0, 8),
+       MSTPCR("scif2", "peripheral_clk", 0, 7),
+       MSTPCR("scif3", "bus_clk", 0, 6),
+       MSTPCR("scif4", "bus_clk", 0, 5),
+       MSTPCR("scif5", "bus_clk", 0, 4),
+       MSTPCR("msiof0", "bus_clk", 0, 2),
+       MSTPCR("msiof1", "bus_clk", 0, 1),
+       MSTPCR("meram0", "sh_clk", 0, 0),
+       MSTPCR("i2c0", "peripheral_clk", 1, 9),
+       MSTPCR("rtc0", "r_clk", 1, 8),
+       MSTPCR("atapi0", "sh_clk", 2, 28),
+       MSTPCR("adc0", "peripheral_clk", 2, 28),
+       MSTPCR("tpu0", "bus_clk", 2, 25),
+       MSTPCR("irda0", "peripheral_clk", 2, 24),
+       MSTPCR("tsif0", "bus_clk", 2, 22),
+       MSTPCR("icb0", "bus_clk", 2, 21),
+       MSTPCR("sdhi0", "bus_clk", 2, 18),
+       MSTPCR("sdhi1", "bus_clk", 2, 17),
+       MSTPCR("keysc0", "r_clk", 2, 14),
+       MSTPCR("usb0", "bus_clk", 2, 11),
+       MSTPCR("2dg0", "bus_clk", 2, 10),
+       MSTPCR("siu0", "bus_clk", 2, 8),
+       MSTPCR("veu1", "bus_clk", 2, 6),
+       MSTPCR("vou0", "bus_clk", 2, 5),
+       MSTPCR("beu0", "bus_clk", 2, 4),
+       MSTPCR("ceu0", "bus_clk", 2, 3),
+       MSTPCR("veu0", "bus_clk", 2, 2),
+       MSTPCR("vpu0", "bus_clk", 2, 1),
+       MSTPCR("lcdc0", "bus_clk", 2, 0),
+#endif
+#if defined(CONFIG_CPU_SUBTYPE_SH7343)
+       MSTPCR("uram0", "umem_clk", 0, 28),
+       MSTPCR("xymem0", "bus_clk", 0, 26),
+       MSTPCR("tmu0", "peripheral_clk", 0, 15),
+       MSTPCR("cmt0", "r_clk", 0, 14),
+       MSTPCR("rwdt0", "r_clk", 0, 13),
+       MSTPCR("scif0", "peripheral_clk", 0, 7),
+       MSTPCR("scif1", "peripheral_clk", 0, 6),
+       MSTPCR("scif2", "peripheral_clk", 0, 5),
+       MSTPCR("scif3", "peripheral_clk", 0, 4),
+       MSTPCR("i2c0", "peripheral_clk", 1, 9),
+       MSTPCR("i2c1", "peripheral_clk", 1, 8),
+       MSTPCR("sdhi0", "peripheral_clk", 2, 18),
+       MSTPCR("keysc0", "r_clk", 2, 14),
+       MSTPCR("usbf0", "peripheral_clk", 2, 11),
+       MSTPCR("siu0", "bus_clk", 2, 8),
+       MSTPCR("jpu0", "bus_clk", 2, 6),
+       MSTPCR("vou0", "bus_clk", 2, 5),
+       MSTPCR("beu0", "bus_clk", 2, 4),
+       MSTPCR("ceu0", "bus_clk", 2, 3),
+       MSTPCR("veu0", "bus_clk", 2, 2),
+       MSTPCR("vpu0", "bus_clk", 2, 1),
+       MSTPCR("lcdc0", "bus_clk", 2, 0),
+#endif
+#if defined(CONFIG_CPU_SUBTYPE_SH7366)
+       /* See page 52 of Datasheet V0.40: Overview -> Block Diagram */
+       MSTPCR("tlb0", "cpu_clk", 0, 31),
+       MSTPCR("ic0", "cpu_clk", 0, 30),
+       MSTPCR("oc0", "cpu_clk", 0, 29),
+       MSTPCR("rsmem0", "sh_clk", 0, 28),
+       MSTPCR("xymem0", "cpu_clk", 0, 26),
+       MSTPCR("intc30", "peripheral_clk", 0, 23),
+       MSTPCR("intc0", "peripheral_clk", 0, 22),
+       MSTPCR("dmac0", "bus_clk", 0, 21),
+       MSTPCR("sh0", "sh_clk", 0, 20),
+       MSTPCR("hudi0", "peripheral_clk", 0, 19),
+       MSTPCR("ubc0", "cpu_clk", 0, 17),
+       MSTPCR("tmu0", "peripheral_clk", 0, 15),
+       MSTPCR("cmt0", "r_clk", 0, 14),
+       MSTPCR("rwdt0", "r_clk", 0, 13),
+       MSTPCR("flctl0", "peripheral_clk", 0, 10),
+       MSTPCR("scif0", "peripheral_clk", 0, 7),
+       MSTPCR("scif1", "bus_clk", 0, 6),
+       MSTPCR("scif2", "bus_clk", 0, 5),
+       MSTPCR("msiof0", "peripheral_clk", 0, 2),
+       MSTPCR("sbr0", "peripheral_clk", 0, 1),
+       MSTPCR("i2c0", "peripheral_clk", 1, 9),
+       MSTPCR("icb0", "bus_clk", 2, 27),
+       MSTPCR("meram0", "sh_clk", 2, 26),
+       MSTPCR("dacc0", "peripheral_clk", 2, 24),
+       MSTPCR("dacy0", "peripheral_clk", 2, 23),
+       MSTPCR("tsif0", "bus_clk", 2, 22),
+       MSTPCR("sdhi0", "bus_clk", 2, 18),
+       MSTPCR("mmcif0", "bus_clk", 2, 17),
+       MSTPCR("usb0", "bus_clk", 2, 11),
+       MSTPCR("siu0", "bus_clk", 2, 8),
+       MSTPCR("veu1", "bus_clk", 2, 7),
+       MSTPCR("vou0", "bus_clk", 2, 5),
+       MSTPCR("beu0", "bus_clk", 2, 4),
+       MSTPCR("ceu0", "bus_clk", 2, 3),
+       MSTPCR("veu0", "bus_clk", 2, 2),
+       MSTPCR("vpu0", "bus_clk", 2, 1),
+       MSTPCR("lcdc0", "bus_clk", 2, 0),
+#endif
 };
 
 static struct clk *sh7722_clocks[] = {
@@ -710,21 +815,30 @@ arch_init_clk_ops(struct clk_ops **ops, int type)
 
 int __init arch_clk_init(void)
 {
-       struct clk *master;
+       struct clk *clk;
        int i;
 
-       master = clk_get(NULL, "master_clk");
+       clk = clk_get(NULL, "master_clk");
        for (i = 0; i < ARRAY_SIZE(sh7722_clocks); i++) {
                pr_debug( "Registering clock '%s'\n", sh7722_clocks[i]->name);
-               sh7722_clocks[i]->parent = master;
+               sh7722_clocks[i]->parent = clk;
                clk_register(sh7722_clocks[i]);
        }
-       clk_put(master);
-
-       for (i = 0; i < ARRAY_SIZE(sh7722_mstpcr); i++) {
-               pr_debug( "Registering mstpcr '%s'\n", sh7722_mstpcr[i].name);
-               clk_register(&sh7722_mstpcr[i]);
+       clk_put(clk);
+
+       clk_register(&sh7722_r_clock);
+
+       for (i = 0; i < ARRAY_SIZE(sh7722_mstpcr_clocks); i++) {
+               pr_debug( "Registering mstpcr clock '%s'\n",
+                         sh7722_mstpcr_clocks[i].name);
+               clk = clk_get(NULL, (void *) sh7722_mstpcr_clocks[i].ops);
+               sh7722_mstpcr_clocks[i].parent = clk;
+               sh7722_mstpcr_clocks[i].ops = &sh7722_mstpcr_clk_ops;
+               clk_register(&sh7722_mstpcr_clocks[i]);
+               clk_put(clk);
        }
 
+       clk_recalc_rate(&sh7722_r_clock); /* make sure rate gets propagated */
+
        return 0;
 }
index 78881b4..0623e37 100644 (file)
@@ -30,6 +30,7 @@ static struct resource iic0_resources[] = {
 
 static struct platform_device iic0_device = {
        .name           = "i2c-sh_mobile",
+       .id             = 0, /* "i2c0" clock */
        .num_resources  = ARRAY_SIZE(iic0_resources),
        .resource       = iic0_resources,
 };
@@ -50,6 +51,7 @@ static struct resource iic1_resources[] = {
 
 static struct platform_device iic1_device = {
        .name           = "i2c-sh_mobile",
+       .id             = 1, /* "i2c1" clock */
        .num_resources  = ARRAY_SIZE(iic1_resources),
        .resource       = iic1_resources,
 };
@@ -115,7 +117,22 @@ static struct plat_sci_port sci_platform_data[] = {
                .mapbase        = 0xffe00000,
                .flags          = UPF_BOOT_AUTOCONF,
                .type           = PORT_SCIF,
-               .irqs           = { 80, 81, 83, 82 },
+               .irqs           = { 80, 80, 80, 80 },
+       }, {
+               .mapbase        = 0xffe10000,
+               .flags          = UPF_BOOT_AUTOCONF,
+               .type           = PORT_SCIF,
+               .irqs           = { 81, 81, 81, 81 },
+       }, {
+               .mapbase        = 0xffe20000,
+               .flags          = UPF_BOOT_AUTOCONF,
+               .type           = PORT_SCIF,
+               .irqs           = { 82, 82, 82, 82 },
+       }, {
+               .mapbase        = 0xffe30000,
+               .flags          = UPF_BOOT_AUTOCONF,
+               .type           = PORT_SCIF,
+               .irqs           = { 83, 83, 83, 83 },
        }, {
                .flags = 0,
        }
@@ -139,18 +156,10 @@ static struct platform_device *sh7343_devices[] __initdata = {
 
 static int __init sh7343_devices_setup(void)
 {
-       clk_always_enable("mstp031"); /* TLB */
-       clk_always_enable("mstp030"); /* IC */
-       clk_always_enable("mstp029"); /* OC */
-       clk_always_enable("mstp028"); /* URAM */
-       clk_always_enable("mstp026"); /* XYMEM */
-       clk_always_enable("mstp023"); /* INTC3 */
-       clk_always_enable("mstp022"); /* INTC */
-       clk_always_enable("mstp020"); /* SuperHyway */
-       clk_always_enable("mstp109"); /* I2C0 */
-       clk_always_enable("mstp108"); /* I2C1 */
-       clk_always_enable("mstp202"); /* VEU */
-       clk_always_enable("mstp201"); /* VPU */
+       clk_always_enable("uram0"); /* URAM */
+       clk_always_enable("xymem0"); /* XYMEM */
+       clk_always_enable("veu0"); /* VEU */
+       clk_always_enable("vpu0"); /* VPU */
 
        platform_resource_setup_memory(&vpu_device, "vpu", 1 << 20);
        platform_resource_setup_memory(&veu_device, "veu", 2 << 20);
@@ -171,7 +180,7 @@ enum {
        MMC_ERR, MMC_TRAN, MMC_FSTAT, MMC_FRDY,
        DMAC4, DMAC5, DMAC_DADERR,
        KEYSC,
-       SCIF, SCIF1, SCIF2, SCIF3, SCIF4,
+       SCIF, SCIF1, SCIF2, SCIF3,
        SIOF0, SIOF1, SIO,
        FLCTL_FLSTEI, FLCTL_FLENDI, FLCTL_FLTREQ0I, FLCTL_FLTREQ1I,
        I2C0_ALI, I2C0_TACKI, I2C0_WAITI, I2C0_DTEI,
index e17db39..839ae97 100644 (file)
@@ -32,6 +32,7 @@ static struct resource iic_resources[] = {
 
 static struct platform_device iic_device = {
        .name           = "i2c-sh_mobile",
+       .id             = 0, /* "i2c0" clock */
        .num_resources  = ARRAY_SIZE(iic_resources),
        .resource       = iic_resources,
 };
@@ -176,19 +177,11 @@ static struct platform_device *sh7366_devices[] __initdata = {
 
 static int __init sh7366_devices_setup(void)
 {
-       clk_always_enable("mstp031"); /* TLB */
-       clk_always_enable("mstp030"); /* IC */
-       clk_always_enable("mstp029"); /* OC */
-       clk_always_enable("mstp028"); /* RSMEM */
-       clk_always_enable("mstp026"); /* XYMEM */
-       clk_always_enable("mstp023"); /* INTC3 */
-       clk_always_enable("mstp022"); /* INTC */
-       clk_always_enable("mstp020"); /* SuperHyway */
-       clk_always_enable("mstp109"); /* I2C */
-       clk_always_enable("mstp211"); /* USB */
-       clk_always_enable("mstp207"); /* VEU-2 */
-       clk_always_enable("mstp202"); /* VEU-1 */
-       clk_always_enable("mstp201"); /* VPU */
+       clk_always_enable("rsmem0"); /* RSMEM */
+       clk_always_enable("xymem0"); /* XYMEM */
+       clk_always_enable("veu1"); /* VEU-2 */
+       clk_always_enable("veu0"); /* VEU-1 */
+       clk_always_enable("vpu0"); /* VPU */
 
        platform_resource_setup_memory(&vpu_device, "vpu", 2 << 20);
        platform_resource_setup_memory(&veu0_device, "veu0", 2 << 20);
index ef77ee1..50cf683 100644 (file)
@@ -62,7 +62,7 @@ static struct resource usbf_resources[] = {
 
 static struct platform_device usbf_device = {
        .name           = "m66592_udc",
-       .id             = -1,
+       .id             = 0, /* "usbf0" clock */
        .dev = {
                .dma_mask               = NULL,
                .coherent_dma_mask      = 0xffffffff,
@@ -87,6 +87,7 @@ static struct resource iic_resources[] = {
 
 static struct platform_device iic_device = {
        .name           = "i2c-sh_mobile",
+       .id             = 0, /* "i2c0" clock */
        .num_resources  = ARRAY_SIZE(iic_resources),
        .resource       = iic_resources,
 };
@@ -147,6 +148,34 @@ static struct platform_device veu_device = {
        .num_resources  = ARRAY_SIZE(veu_resources),
 };
 
+static struct uio_info jpu_platform_data = {
+       .name = "JPU",
+       .version = "0",
+       .irq = 27,
+};
+
+static struct resource jpu_resources[] = {
+       [0] = {
+               .name   = "JPU",
+               .start  = 0xfea00000,
+               .end    = 0xfea102d0,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               /* place holder for contiguous memory */
+       },
+};
+
+static struct platform_device jpu_device = {
+       .name           = "uio_pdrv_genirq",
+       .id             = 2,
+       .dev = {
+               .platform_data  = &jpu_platform_data,
+       },
+       .resource       = jpu_resources,
+       .num_resources  = ARRAY_SIZE(jpu_resources),
+};
+
 static struct plat_sci_port sci_platform_data[] = {
        {
                .mapbase        = 0xffe00000,
@@ -186,24 +215,21 @@ static struct platform_device *sh7722_devices[] __initdata = {
        &sci_device,
        &vpu_device,
        &veu_device,
+       &jpu_device,
 };
 
 static int __init sh7722_devices_setup(void)
 {
-       clk_always_enable("mstp031"); /* TLB */
-       clk_always_enable("mstp030"); /* IC */
-       clk_always_enable("mstp029"); /* OC */
-       clk_always_enable("mstp028"); /* URAM */
-       clk_always_enable("mstp026"); /* XYMEM */
-       clk_always_enable("mstp022"); /* INTC */
-       clk_always_enable("mstp020"); /* SuperHyway */
-       clk_always_enable("mstp109"); /* I2C */
-       clk_always_enable("mstp211"); /* USB */
-       clk_always_enable("mstp202"); /* VEU */
-       clk_always_enable("mstp201"); /* VPU */
+       clk_always_enable("uram0"); /* URAM */
+       clk_always_enable("xymem0"); /* XYMEM */
+       clk_always_enable("rtc0"); /* RTC */
+       clk_always_enable("veu0"); /* VEU */
+       clk_always_enable("vpu0"); /* VPU */
+       clk_always_enable("jpu0"); /* JPU */
 
        platform_resource_setup_memory(&vpu_device, "vpu", 1 << 20);
        platform_resource_setup_memory(&veu_device, "veu", 2 << 20);
+       platform_resource_setup_memory(&jpu_device, "jpu", 2 << 20);
 
        return platform_add_devices(sh7722_devices,
                                    ARRAY_SIZE(sh7722_devices));
index 6d9e697..849770d 100644 (file)
@@ -215,6 +215,7 @@ static struct resource iic_resources[] = {
 
 static struct platform_device iic_device = {
        .name           = "i2c-sh_mobile",
+       .id             = 0, /* "i2c0" clock */
        .num_resources  = ARRAY_SIZE(iic_resources),
        .resource       = iic_resources,
 };
@@ -231,19 +232,11 @@ static struct platform_device *sh7723_devices[] __initdata = {
 
 static int __init sh7723_devices_setup(void)
 {
-       clk_always_enable("mstp031"); /* TLB */
-       clk_always_enable("mstp030"); /* IC */
-       clk_always_enable("mstp029"); /* OC */
-       clk_always_enable("mstp024"); /* FPU */
-       clk_always_enable("mstp022"); /* INTC */
-       clk_always_enable("mstp020"); /* SuperHyway */
-       clk_always_enable("mstp000"); /* MERAM */
-       clk_always_enable("mstp109"); /* I2C */
-       clk_always_enable("mstp108"); /* RTC */
-       clk_always_enable("mstp211"); /* USB */
-       clk_always_enable("mstp206"); /* VEU2H1 */
-       clk_always_enable("mstp202"); /* VEU2H0 */
-       clk_always_enable("mstp201"); /* VPU */
+       clk_always_enable("meram0"); /* MERAM */
+       clk_always_enable("rtc0"); /* RTC */
+       clk_always_enable("veu1"); /* VEU2H1 */
+       clk_always_enable("veu0"); /* VEU2H0 */
+       clk_always_enable("vpu0"); /* VPU */
 
        platform_resource_setup_memory(&vpu_device, "vpu", 2 << 20);
        platform_resource_setup_memory(&veu0_device, "veu0", 2 << 20);
index 13b6674..5917413 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Debug trap jump tables for SuperH
  *
- *  Copyright (C) 2006  Paul Mundt
+ *  Copyright (C) 2006 - 2008  Paul Mundt
  *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file "COPYING" in the main directory of this archive
 #include <linux/sys.h>
 #include <linux/linkage.h>
 
-#if !defined(CONFIG_SH_KGDB)
-#define kgdb_handle_exception  debug_trap_handler
+#if !defined(CONFIG_KGDB)
+#define breakpoint_trap_handler                debug_trap_handler
+#define singlestep_trap_handler                debug_trap_handler
 #endif
 
 #if !defined(CONFIG_SH_STANDARD_BIOS)
-#define sh_bios_handler                debug_trap_handler
+#define sh_bios_handler                        debug_trap_handler
 #endif
 
        .data
@@ -35,7 +36,7 @@ ENTRY(debug_trap_table)
        .long debug_trap_handler        /* 0x39 */
        .long debug_trap_handler        /* 0x3a */
        .long debug_trap_handler        /* 0x3b */
-       .long kgdb_handle_exception     /* 0x3c */
-       .long debug_trap_handler        /* 0x3d */
+       .long breakpoint_trap_handler   /* 0x3c */
+       .long singlestep_trap_handler   /* 0x3d */
        .long bug_trap_handler          /* 0x3e */
        .long sh_bios_handler           /* 0x3f */
diff --git a/arch/sh/kernel/disassemble.c b/arch/sh/kernel/disassemble.c
new file mode 100644 (file)
index 0000000..64d5d8d
--- /dev/null
@@ -0,0 +1,573 @@
+/*
+ * Disassemble SuperH instructions.
+ *
+ * Copyright (C) 1999 kaz Kojima
+ * Copyright (C) 2008 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+
+/*
+ * Format of an instruction in memory.
+ */
+typedef enum {
+       HEX_0, HEX_1, HEX_2, HEX_3, HEX_4, HEX_5, HEX_6, HEX_7,
+       HEX_8, HEX_9, HEX_A, HEX_B, HEX_C, HEX_D, HEX_E, HEX_F,
+       REG_N, REG_M, REG_NM, REG_B,
+       BRANCH_12, BRANCH_8,
+       DISP_8, DISP_4,
+       IMM_4, IMM_4BY2, IMM_4BY4, PCRELIMM_8BY2, PCRELIMM_8BY4,
+       IMM_8, IMM_8BY2, IMM_8BY4,
+} sh_nibble_type;
+
+typedef enum {
+       A_END, A_BDISP12, A_BDISP8,
+       A_DEC_M, A_DEC_N,
+       A_DISP_GBR, A_DISP_PC, A_DISP_REG_M, A_DISP_REG_N,
+       A_GBR,
+       A_IMM,
+       A_INC_M, A_INC_N,
+       A_IND_M, A_IND_N, A_IND_R0_REG_M, A_IND_R0_REG_N,
+       A_MACH, A_MACL,
+       A_PR, A_R0, A_R0_GBR, A_REG_M, A_REG_N, A_REG_B,
+       A_SR, A_VBR, A_SSR, A_SPC, A_SGR, A_DBR,
+       F_REG_N, F_REG_M, D_REG_N, D_REG_M,
+       X_REG_N, /* Only used for argument parsing */
+       X_REG_M, /* Only used for argument parsing */
+       DX_REG_N, DX_REG_M, V_REG_N, V_REG_M,
+       FD_REG_N,
+       XMTRX_M4,
+       F_FR0,
+       FPUL_N, FPUL_M, FPSCR_N, FPSCR_M,
+} sh_arg_type;
+
+static struct sh_opcode_info {
+       char *name;
+       sh_arg_type arg[7];
+       sh_nibble_type nibbles[4];
+} sh_table[] = {
+       {"add",{A_IMM,A_REG_N},{HEX_7,REG_N,IMM_8}},
+       {"add",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_C}},
+       {"addc",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_E}},
+       {"addv",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_F}},
+       {"and",{A_IMM,A_R0},{HEX_C,HEX_9,IMM_8}},
+       {"and",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_9}},
+       {"and.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_D,IMM_8}},
+       {"bra",{A_BDISP12},{HEX_A,BRANCH_12}},
+       {"bsr",{A_BDISP12},{HEX_B,BRANCH_12}},
+       {"bt",{A_BDISP8},{HEX_8,HEX_9,BRANCH_8}},
+       {"bf",{A_BDISP8},{HEX_8,HEX_B,BRANCH_8}},
+       {"bt.s",{A_BDISP8},{HEX_8,HEX_D,BRANCH_8}},
+       {"bt/s",{A_BDISP8},{HEX_8,HEX_D,BRANCH_8}},
+       {"bf.s",{A_BDISP8},{HEX_8,HEX_F,BRANCH_8}},
+       {"bf/s",{A_BDISP8},{HEX_8,HEX_F,BRANCH_8}},
+       {"clrmac",{0},{HEX_0,HEX_0,HEX_2,HEX_8}},
+       {"clrs",{0},{HEX_0,HEX_0,HEX_4,HEX_8}},
+       {"clrt",{0},{HEX_0,HEX_0,HEX_0,HEX_8}},
+       {"cmp/eq",{A_IMM,A_R0},{HEX_8,HEX_8,IMM_8}},
+       {"cmp/eq",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_0}},
+       {"cmp/ge",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_3}},
+       {"cmp/gt",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_7}},
+       {"cmp/hi",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_6}},
+       {"cmp/hs",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_2}},
+       {"cmp/pl",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_5}},
+       {"cmp/pz",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_1}},
+       {"cmp/str",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_C}},
+       {"div0s",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_7}},
+       {"div0u",{0},{HEX_0,HEX_0,HEX_1,HEX_9}},
+       {"div1",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_4}},
+       {"exts.b",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_E}},
+       {"exts.w",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_F}},
+       {"extu.b",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_C}},
+       {"extu.w",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_D}},
+       {"jmp",{A_IND_N},{HEX_4,REG_N,HEX_2,HEX_B}},
+       {"jsr",{A_IND_N},{HEX_4,REG_N,HEX_0,HEX_B}},
+       {"ldc",{A_REG_N,A_SR},{HEX_4,REG_N,HEX_0,HEX_E}},
+       {"ldc",{A_REG_N,A_GBR},{HEX_4,REG_N,HEX_1,HEX_E}},
+       {"ldc",{A_REG_N,A_VBR},{HEX_4,REG_N,HEX_2,HEX_E}},
+       {"ldc",{A_REG_N,A_SSR},{HEX_4,REG_N,HEX_3,HEX_E}},
+       {"ldc",{A_REG_N,A_SPC},{HEX_4,REG_N,HEX_4,HEX_E}},
+       {"ldc",{A_REG_N,A_DBR},{HEX_4,REG_N,HEX_7,HEX_E}},
+       {"ldc",{A_REG_N,A_REG_B},{HEX_4,REG_N,REG_B,HEX_E}},
+       {"ldc.l",{A_INC_N,A_SR},{HEX_4,REG_N,HEX_0,HEX_7}},
+       {"ldc.l",{A_INC_N,A_GBR},{HEX_4,REG_N,HEX_1,HEX_7}},
+       {"ldc.l",{A_INC_N,A_VBR},{HEX_4,REG_N,HEX_2,HEX_7}},
+       {"ldc.l",{A_INC_N,A_SSR},{HEX_4,REG_N,HEX_3,HEX_7}},
+       {"ldc.l",{A_INC_N,A_SPC},{HEX_4,REG_N,HEX_4,HEX_7}},
+       {"ldc.l",{A_INC_N,A_DBR},{HEX_4,REG_N,HEX_7,HEX_7}},
+       {"ldc.l",{A_INC_N,A_REG_B},{HEX_4,REG_N,REG_B,HEX_7}},
+       {"lds",{A_REG_N,A_MACH},{HEX_4,REG_N,HEX_0,HEX_A}},
+       {"lds",{A_REG_N,A_MACL},{HEX_4,REG_N,HEX_1,HEX_A}},
+       {"lds",{A_REG_N,A_PR},{HEX_4,REG_N,HEX_2,HEX_A}},
+       {"lds",{A_REG_M,FPUL_N},{HEX_4,REG_M,HEX_5,HEX_A}},
+       {"lds",{A_REG_M,FPSCR_N},{HEX_4,REG_M,HEX_6,HEX_A}},
+       {"lds.l",{A_INC_N,A_MACH},{HEX_4,REG_N,HEX_0,HEX_6}},
+       {"lds.l",{A_INC_N,A_MACL},{HEX_4,REG_N,HEX_1,HEX_6}},
+       {"lds.l",{A_INC_N,A_PR},{HEX_4,REG_N,HEX_2,HEX_6}},
+       {"lds.l",{A_INC_M,FPUL_N},{HEX_4,REG_M,HEX_5,HEX_6}},
+       {"lds.l",{A_INC_M,FPSCR_N},{HEX_4,REG_M,HEX_6,HEX_6}},
+       {"ldtlb",{0},{HEX_0,HEX_0,HEX_3,HEX_8}},
+       {"mac.w",{A_INC_M,A_INC_N},{HEX_4,REG_N,REG_M,HEX_F}},
+       {"mov",{A_IMM,A_REG_N},{HEX_E,REG_N,IMM_8}},
+       {"mov",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_3}},
+       {"mov.b",{ A_REG_M,A_IND_R0_REG_N},{HEX_0,REG_N,REG_M,HEX_4}},
+       {"mov.b",{ A_REG_M,A_DEC_N},{HEX_2,REG_N,REG_M,HEX_4}},
+       {"mov.b",{ A_REG_M,A_IND_N},{HEX_2,REG_N,REG_M,HEX_0}},
+       {"mov.b",{A_DISP_REG_M,A_R0},{HEX_8,HEX_4,REG_M,IMM_4}},
+       {"mov.b",{A_DISP_GBR,A_R0},{HEX_C,HEX_4,IMM_8}},
+       {"mov.b",{A_IND_R0_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_C}},
+       {"mov.b",{A_INC_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_4}},
+       {"mov.b",{A_IND_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_0}},
+       {"mov.b",{A_R0,A_DISP_REG_M},{HEX_8,HEX_0,REG_M,IMM_4}},
+       {"mov.b",{A_R0,A_DISP_GBR},{HEX_C,HEX_0,IMM_8}},
+       {"mov.l",{ A_REG_M,A_DISP_REG_N},{HEX_1,REG_N,REG_M,IMM_4BY4}},
+       {"mov.l",{ A_REG_M,A_IND_R0_REG_N},{HEX_0,REG_N,REG_M,HEX_6}},
+       {"mov.l",{ A_REG_M,A_DEC_N},{HEX_2,REG_N,REG_M,HEX_6}},
+       {"mov.l",{ A_REG_M,A_IND_N},{HEX_2,REG_N,REG_M,HEX_2}},
+       {"mov.l",{A_DISP_REG_M,A_REG_N},{HEX_5,REG_N,REG_M,IMM_4BY4}},
+       {"mov.l",{A_DISP_GBR,A_R0},{HEX_C,HEX_6,IMM_8BY4}},
+       {"mov.l",{A_DISP_PC,A_REG_N},{HEX_D,REG_N,PCRELIMM_8BY4}},
+       {"mov.l",{A_IND_R0_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_E}},
+       {"mov.l",{A_INC_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_6}},
+       {"mov.l",{A_IND_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_2}},
+       {"mov.l",{A_R0,A_DISP_GBR},{HEX_C,HEX_2,IMM_8BY4}},
+       {"mov.w",{ A_REG_M,A_IND_R0_REG_N},{HEX_0,REG_N,REG_M,HEX_5}},
+       {"mov.w",{ A_REG_M,A_DEC_N},{HEX_2,REG_N,REG_M,HEX_5}},
+       {"mov.w",{ A_REG_M,A_IND_N},{HEX_2,REG_N,REG_M,HEX_1}},
+       {"mov.w",{A_DISP_REG_M,A_R0},{HEX_8,HEX_5,REG_M,IMM_4BY2}},
+       {"mov.w",{A_DISP_GBR,A_R0},{HEX_C,HEX_5,IMM_8BY2}},
+       {"mov.w",{A_DISP_PC,A_REG_N},{HEX_9,REG_N,PCRELIMM_8BY2}},
+       {"mov.w",{A_IND_R0_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_D}},
+       {"mov.w",{A_INC_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_5}},
+       {"mov.w",{A_IND_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_1}},
+       {"mov.w",{A_R0,A_DISP_REG_M},{HEX_8,HEX_1,REG_M,IMM_4BY2}},
+       {"mov.w",{A_R0,A_DISP_GBR},{HEX_C,HEX_1,IMM_8BY2}},
+       {"mova",{A_DISP_PC,A_R0},{HEX_C,HEX_7,PCRELIMM_8BY4}},
+       {"movca.l",{A_R0,A_IND_N},{HEX_0,REG_N,HEX_C,HEX_3}},
+       {"movt",{A_REG_N},{HEX_0,REG_N,HEX_2,HEX_9}},
+       {"muls",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_F}},
+       {"mul.l",{ A_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_7}},
+       {"mulu",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_E}},
+       {"neg",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_B}},
+       {"negc",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_A}},
+       {"nop",{0},{HEX_0,HEX_0,HEX_0,HEX_9}},
+       {"not",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_7}},
+       {"ocbi",{A_IND_N},{HEX_0,REG_N,HEX_9,HEX_3}},
+       {"ocbp",{A_IND_N},{HEX_0,REG_N,HEX_A,HEX_3}},
+       {"ocbwb",{A_IND_N},{HEX_0,REG_N,HEX_B,HEX_3}},
+       {"or",{A_IMM,A_R0},{HEX_C,HEX_B,IMM_8}},
+       {"or",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_B}},
+       {"or.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_F,IMM_8}},
+       {"pref",{A_IND_N},{HEX_0,REG_N,HEX_8,HEX_3}},
+       {"rotcl",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_4}},
+       {"rotcr",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_5}},
+       {"rotl",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_4}},
+       {"rotr",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_5}},
+       {"rte",{0},{HEX_0,HEX_0,HEX_2,HEX_B}},
+       {"rts",{0},{HEX_0,HEX_0,HEX_0,HEX_B}},
+       {"sets",{0},{HEX_0,HEX_0,HEX_5,HEX_8}},
+       {"sett",{0},{HEX_0,HEX_0,HEX_1,HEX_8}},
+       {"shad",{ A_REG_M,A_REG_N},{HEX_4,REG_N,REG_M,HEX_C}},
+       {"shld",{ A_REG_M,A_REG_N},{HEX_4,REG_N,REG_M,HEX_D}},
+       {"shal",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_0}},
+       {"shar",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_1}},
+       {"shll",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_0}},
+       {"shll16",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_8}},
+       {"shll2",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_8}},
+       {"shll8",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_8}},
+       {"shlr",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_1}},
+       {"shlr16",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_9}},
+       {"shlr2",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_9}},
+       {"shlr8",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_9}},
+       {"sleep",{0},{HEX_0,HEX_0,HEX_1,HEX_B}},
+       {"stc",{A_SR,A_REG_N},{HEX_0,REG_N,HEX_0,HEX_2}},
+       {"stc",{A_GBR,A_REG_N},{HEX_0,REG_N,HEX_1,HEX_2}},
+       {"stc",{A_VBR,A_REG_N},{HEX_0,REG_N,HEX_2,HEX_2}},
+       {"stc",{A_SSR,A_REG_N},{HEX_0,REG_N,HEX_3,HEX_2}},
+       {"stc",{A_SPC,A_REG_N},{HEX_0,REG_N,HEX_4,HEX_2}},
+       {"stc",{A_SGR,A_REG_N},{HEX_0,REG_N,HEX_6,HEX_2}},
+       {"stc",{A_DBR,A_REG_N},{HEX_0,REG_N,HEX_7,HEX_2}},
+       {"stc",{A_REG_B,A_REG_N},{HEX_0,REG_N,REG_B,HEX_2}},
+       {"stc.l",{A_SR,A_DEC_N},{HEX_4,REG_N,HEX_0,HEX_3}},
+       {"stc.l",{A_GBR,A_DEC_N},{HEX_4,REG_N,HEX_1,HEX_3}},
+       {"stc.l",{A_VBR,A_DEC_N},{HEX_4,REG_N,HEX_2,HEX_3}},
+       {"stc.l",{A_SSR,A_DEC_N},{HEX_4,REG_N,HEX_3,HEX_3}},
+       {"stc.l",{A_SPC,A_DEC_N},{HEX_4,REG_N,HEX_4,HEX_3}},
+       {"stc.l",{A_SGR,A_DEC_N},{HEX_4,REG_N,HEX_6,HEX_3}},
+       {"stc.l",{A_DBR,A_DEC_N},{HEX_4,REG_N,HEX_7,HEX_3}},
+       {"stc.l",{A_REG_B,A_DEC_N},{HEX_4,REG_N,REG_B,HEX_3}},
+       {"sts",{A_MACH,A_REG_N},{HEX_0,REG_N,HEX_0,HEX_A}},
+       {"sts",{A_MACL,A_REG_N},{HEX_0,REG_N,HEX_1,HEX_A}},
+       {"sts",{A_PR,A_REG_N},{HEX_0,REG_N,HEX_2,HEX_A}},
+       {"sts",{FPUL_M,A_REG_N},{HEX_0,REG_N,HEX_5,HEX_A}},
+       {"sts",{FPSCR_M,A_REG_N},{HEX_0,REG_N,HEX_6,HEX_A}},
+       {"sts.l",{A_MACH,A_DEC_N},{HEX_4,REG_N,HEX_0,HEX_2}},
+       {"sts.l",{A_MACL,A_DEC_N},{HEX_4,REG_N,HEX_1,HEX_2}},
+       {"sts.l",{A_PR,A_DEC_N},{HEX_4,REG_N,HEX_2,HEX_2}},
+       {"sts.l",{FPUL_M,A_DEC_N},{HEX_4,REG_N,HEX_5,HEX_2}},
+       {"sts.l",{FPSCR_M,A_DEC_N},{HEX_4,REG_N,HEX_6,HEX_2}},
+       {"sub",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_8}},
+       {"subc",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_A}},
+       {"subv",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_B}},
+       {"swap.b",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_8}},
+       {"swap.w",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_9}},
+       {"tas.b",{A_IND_N},{HEX_4,REG_N,HEX_1,HEX_B}},
+       {"trapa",{A_IMM},{HEX_C,HEX_3,IMM_8}},
+       {"tst",{A_IMM,A_R0},{HEX_C,HEX_8,IMM_8}},
+       {"tst",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_8}},
+       {"tst.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_C,IMM_8}},
+       {"xor",{A_IMM,A_R0},{HEX_C,HEX_A,IMM_8}},
+       {"xor",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_A}},
+       {"xor.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_E,IMM_8}},
+       {"xtrct",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_D}},
+       {"mul.l",{ A_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_7}},
+       {"dt",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_0}},
+       {"dmuls.l",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_D}},
+       {"dmulu.l",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_5}},
+       {"mac.l",{A_INC_M,A_INC_N},{HEX_0,REG_N,REG_M,HEX_F}},
+       {"braf",{A_REG_N},{HEX_0,REG_N,HEX_2,HEX_3}},
+       {"bsrf",{A_REG_N},{HEX_0,REG_N,HEX_0,HEX_3}},
+       {"fabs",{FD_REG_N},{HEX_F,REG_N,HEX_5,HEX_D}},
+       {"fadd",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_0}},
+       {"fadd",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_0}},
+       {"fcmp/eq",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_4}},
+       {"fcmp/eq",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_4}},
+       {"fcmp/gt",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_5}},
+       {"fcmp/gt",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_5}},
+       {"fcnvds",{D_REG_N,FPUL_M},{HEX_F,REG_N,HEX_B,HEX_D}},
+       {"fcnvsd",{FPUL_M,D_REG_N},{HEX_F,REG_N,HEX_A,HEX_D}},
+       {"fdiv",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_3}},
+       {"fdiv",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_3}},
+       {"fipr",{V_REG_M,V_REG_N},{HEX_F,REG_NM,HEX_E,HEX_D}},
+       {"fldi0",{F_REG_N},{HEX_F,REG_N,HEX_8,HEX_D}},
+       {"fldi1",{F_REG_N},{HEX_F,REG_N,HEX_9,HEX_D}},
+       {"flds",{F_REG_N,FPUL_M},{HEX_F,REG_N,HEX_1,HEX_D}},
+       {"float",{FPUL_M,FD_REG_N},{HEX_F,REG_N,HEX_2,HEX_D}},
+       {"fmac",{F_FR0,F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_E}},
+       {"fmov",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_C}},
+       {"fmov",{DX_REG_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_C}},
+       {"fmov",{A_IND_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_8}},
+       {"fmov",{A_IND_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_8}},
+       {"fmov",{F_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}},
+       {"fmov",{DX_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}},
+       {"fmov",{A_INC_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_9}},
+       {"fmov",{A_INC_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_9}},
+       {"fmov",{F_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}},
+       {"fmov",{DX_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}},
+       {"fmov",{A_IND_R0_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_6}},
+       {"fmov",{A_IND_R0_REG_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_6}},
+       {"fmov",{F_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}},
+       {"fmov",{DX_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}},
+       {"fmov.d",{A_IND_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_8}},
+       {"fmov.d",{DX_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}},
+       {"fmov.d",{A_INC_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_9}},
+       {"fmov.d",{DX_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}},
+       {"fmov.d",{A_IND_R0_REG_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_6}},
+       {"fmov.d",{DX_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}},
+       {"fmov.s",{A_IND_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_8}},
+       {"fmov.s",{F_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}},
+       {"fmov.s",{A_INC_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_9}},
+       {"fmov.s",{F_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}},
+       {"fmov.s",{A_IND_R0_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_6}},
+       {"fmov.s",{F_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}},
+       {"fmul",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_2}},
+       {"fmul",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_2}},
+       {"fneg",{FD_REG_N},{HEX_F,REG_N,HEX_4,HEX_D}},
+       {"frchg",{0},{HEX_F,HEX_B,HEX_F,HEX_D}},
+       {"fschg",{0},{HEX_F,HEX_3,HEX_F,HEX_D}},
+       {"fsqrt",{FD_REG_N},{HEX_F,REG_N,HEX_6,HEX_D}},
+       {"fsts",{FPUL_M,F_REG_N},{HEX_F,REG_N,HEX_0,HEX_D}},
+       {"fsub",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_1}},
+       {"fsub",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_1}},
+       {"ftrc",{FD_REG_N,FPUL_M},{HEX_F,REG_N,HEX_3,HEX_D}},
+       {"ftrv",{XMTRX_M4,V_REG_N},{HEX_F,REG_NM,HEX_F,HEX_D}},
+       { 0 },
+};
+
+static void print_sh_insn(u32 memaddr, u16 insn)
+{
+       int relmask = ~0;
+       int nibs[4] = { (insn >> 12) & 0xf, (insn >> 8) & 0xf, (insn >> 4) & 0xf, insn & 0xf};
+       int lastsp;
+       struct sh_opcode_info *op = sh_table;
+
+       for (; op->name; op++) {
+               int n;
+               int imm = 0;
+               int rn = 0;
+               int rm = 0;
+               int rb = 0;
+               int disp_pc;
+               int disp_pc_addr = 0;
+
+               for (n = 0; n < 4; n++) {
+                       int i = op->nibbles[n];
+
+                       if (i < 16) {
+                               if (nibs[n] == i)
+                                       continue;
+                               goto fail;
+                       }
+                       switch (i) {
+                       case BRANCH_8:
+                               imm = (nibs[2] << 4) | (nibs[3]);
+                               if (imm & 0x80)
+                                       imm |= ~0xff;
+                               imm = ((char)imm) * 2 + 4 ;
+                               goto ok;
+                       case BRANCH_12:
+                               imm = ((nibs[1]) << 8) | (nibs[2] << 4) | (nibs[3]);
+                               if (imm & 0x800)
+                                       imm |= ~0xfff;
+                               imm = imm * 2 + 4;
+                               goto ok;
+                       case IMM_4:
+                               imm = nibs[3];
+                               goto ok;
+                       case IMM_4BY2:
+                               imm = nibs[3] <<1;
+                               goto ok;
+                       case IMM_4BY4:
+                               imm = nibs[3] <<2;
+                               goto ok;
+                       case IMM_8:
+                               imm = (nibs[2] << 4) | nibs[3];
+                               goto ok;
+                       case PCRELIMM_8BY2:
+                               imm = ((nibs[2] << 4) | nibs[3]) <<1;
+                               relmask = ~1;
+                               goto ok;
+                       case PCRELIMM_8BY4:
+                               imm = ((nibs[2] << 4) | nibs[3]) <<2;
+                               relmask = ~3;
+                               goto ok;
+                       case IMM_8BY2:
+                               imm = ((nibs[2] << 4) | nibs[3]) <<1;
+                               goto ok;
+                       case IMM_8BY4:
+                               imm = ((nibs[2] << 4) | nibs[3]) <<2;
+                               goto ok;
+                       case DISP_8:
+                               imm = (nibs[2] << 4) | (nibs[3]);
+                               goto ok;
+                       case DISP_4:
+                               imm = nibs[3];
+                               goto ok;
+                       case REG_N:
+                               rn = nibs[n];
+                               break;
+                       case REG_M:
+                               rm = nibs[n];
+                               break;
+                       case REG_NM:
+                               rn = (nibs[n] & 0xc) >> 2;
+                               rm = (nibs[n] & 0x3);
+                               break;
+                       case REG_B:
+                               rb = nibs[n] & 0x07;
+                               break;
+                       default:
+                               return;
+                       }
+               }
+
+       ok:
+               printk("%-8s  ", op->name);
+               lastsp = (op->arg[0] == A_END);
+               disp_pc = 0;
+               for (n = 0; n < 6 && op->arg[n] != A_END; n++) {
+                       if (n && op->arg[1] != A_END)
+                               printk(", ");
+                       switch (op->arg[n]) {
+                       case A_IMM:
+                               printk("#%d", (char)(imm));
+                               break;
+                       case A_R0:
+                               printk("r0");
+                               break;
+                       case A_REG_N:
+                               printk("r%d", rn);
+                               break;
+                       case A_INC_N:
+                               printk("@r%d+", rn);
+                               break;
+                       case A_DEC_N:
+                               printk("@-r%d", rn);
+                               break;
+                       case A_IND_N:
+                               printk("@r%d", rn);
+                               break;
+                       case A_DISP_REG_N:
+                               printk("@(%d,r%d)", imm, rn);
+                               break;
+                       case A_REG_M:
+                               printk("r%d", rm);
+                               break;
+                       case A_INC_M:
+                               printk("@r%d+", rm);
+                               break;
+                       case A_DEC_M:
+                               printk("@-r%d", rm);
+                               break;
+                       case A_IND_M:
+                               printk("@r%d", rm);
+                               break;
+                       case A_DISP_REG_M:
+                               printk("@(%d,r%d)", imm, rm);
+                               break;
+                       case A_REG_B:
+                               printk("r%d_bank", rb);
+                               break;
+                       case A_DISP_PC:
+                               disp_pc = 1;
+                               disp_pc_addr = imm + 4 + (memaddr & relmask);
+                               printk("%08x <%pS>", disp_pc_addr,
+                                      (void *)disp_pc_addr);
+                               break;
+                       case A_IND_R0_REG_N:
+                               printk("@(r0,r%d)", rn);
+                               break;
+                       case A_IND_R0_REG_M:
+                               printk("@(r0,r%d)", rm);
+                               break;
+                       case A_DISP_GBR:
+                               printk("@(%d,gbr)",imm);
+                               break;
+                       case A_R0_GBR:
+                               printk("@(r0,gbr)");
+                               break;
+                       case A_BDISP12:
+                       case A_BDISP8:
+                               printk("%08x", imm + memaddr);
+                               break;
+                       case A_SR:
+                               printk("sr");
+                               break;
+                       case A_GBR:
+                               printk("gbr");
+                               break;
+                       case A_VBR:
+                               printk("vbr");
+                               break;
+                       case A_SSR:
+                               printk("ssr");
+                               break;
+                       case A_SPC:
+                               printk("spc");
+                               break;
+                       case A_MACH:
+                               printk("mach");
+                               break;
+                       case A_MACL:
+                               printk("macl");
+                               break;
+                       case A_PR:
+                               printk("pr");
+                               break;
+                       case A_SGR:
+                               printk("sgr");
+                               break;
+                       case A_DBR:
+                               printk("dbr");
+                               break;
+                       case FD_REG_N:
+                               if (0)
+                                       goto d_reg_n;
+                       case F_REG_N:
+                               printk("fr%d", rn);
+                               break;
+                       case F_REG_M:
+                               printk("fr%d", rm);
+                               break;
+                       case DX_REG_N:
+                               if (rn & 1) {
+                                       printk("xd%d", rn & ~1);
+                                       break;
+                               }
+                       d_reg_n:
+                       case D_REG_N:
+                               printk("dr%d", rn);
+                               break;
+                       case DX_REG_M:
+                               if (rm & 1) {
+                                       printk("xd%d", rm & ~1);
+                                       break;
+                               }
+                       case D_REG_M:
+                               printk("dr%d", rm);
+                               break;
+                       case FPSCR_M:
+                       case FPSCR_N:
+                               printk("fpscr");
+                               break;
+                       case FPUL_M:
+                       case FPUL_N:
+                               printk("fpul");
+                               break;
+                       case F_FR0:
+                               printk("fr0");
+                               break;
+                       case V_REG_N:
+                               printk("fv%d", rn*4);
+                               break;
+                       case V_REG_M:
+                               printk("fv%d", rm*4);
+                               break;
+                       case XMTRX_M4:
+                               printk("xmtrx");
+                               break;
+                       default:
+                               return;
+                       }
+               }
+
+               if (disp_pc && strcmp(op->name, "mova") != 0) {
+                       u32 val;
+
+                       if (relmask == ~1)
+                               __get_user(val, (u16 *)disp_pc_addr);
+                       else
+                               __get_user(val, (u32 *)disp_pc_addr);
+
+                       printk("  ! %08x <%pS>", val, (void *)val);
+               }
+
+               return;
+       fail:
+               ;
+
+       }
+
+       printk(".word 0x%x%x%x%x", nibs[0], nibs[1], nibs[2], nibs[3]);
+}
+
+void show_code(struct pt_regs *regs)
+{
+       unsigned short *pc = (unsigned short *)regs->pc;
+       long i;
+
+       if (regs->pc & 0x1)
+               return;
+
+       printk("Code:\n");
+
+       for (i = -3 ; i < 6 ; i++) {
+               unsigned short insn;
+
+               if (__get_user(insn, pc + i)) {
+                       printk(" (Bad address in pc)\n");
+                       break;
+               }
+
+               printk("%s%08lx:  ", (i ? "  ": "->"), (unsigned long)(pc + i));
+               print_sh_insn((unsigned long)(pc + i), insn);
+               printk("\n");
+       }
+
+       printk("\n");
+}
index 5b7efc4..d62359c 100644 (file)
@@ -308,15 +308,19 @@ ENTRY(system_call)
        mov.l   1f, r9
        mov.l   @r9, r8         ! Read from TRA (Trap Address) Register
 #endif
+
+       mov     #OFF_TRA, r10
+       add     r15, r10
+       mov.l   r8, @r10                ! set TRA value to tra
+
        /*
         * Check the trap type
         */
        mov     #((0x20 << 2) - 1), r9
        cmp/hi  r9, r8
        bt/s    debug_trap              ! it's a debug trap..
-        mov    #OFF_TRA, r9
-       add     r15, r9
-       mov.l   r8, @r9                 ! set TRA value to tra
+        nop
+
 #ifdef CONFIG_TRACE_IRQFLAGS
        mov.l   5f, r10
        jsr     @r10
@@ -371,47 +375,3 @@ syscall_exit:
 #endif
 7:     .long   do_syscall_trace_enter
 8:     .long   do_syscall_trace_leave
-
-#ifdef CONFIG_FUNCTION_TRACER
-       .align 2
-       .globl  _mcount
-       .type   _mcount,@function
-       .globl  mcount
-       .type   mcount,@function
-_mcount:
-mcount:
-       mov.l   r4, @-r15
-       mov.l   r5, @-r15
-       mov.l   r6, @-r15
-       mov.l   r7, @-r15
-       sts.l   pr, @-r15
-
-       mov.l   @(20,r15),r4
-       sts     pr, r5
-
-       mov.l   1f, r6
-       mov.l   ftrace_stub, r7 
-       cmp/eq  r6, r7
-       bt      skip_trace
-
-       mov.l   @r6, r6
-       jsr     @r6
-        nop
-
-skip_trace:
-
-       lds.l   @r15+, pr
-       mov.l   @r15+, r7
-       mov.l   @r15+, r6
-       mov.l   @r15+, r5
-       rts
-        mov.l  @r15+, r4
-
-       .align 2
-1:     .long   ftrace_trace_function
-
-       .globl  ftrace_stub
-ftrace_stub:
-       rts
-        nop
-#endif /* CONFIG_FUNCTION_TRACER */
diff --git a/arch/sh/kernel/ftrace.c b/arch/sh/kernel/ftrace.c
new file mode 100644 (file)
index 0000000..4c32474
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2008 Matt Fleming <mjf@gentoo.org>
+ * Copyright (C) 2008 Paul Mundt <lethal@linux-sh.org>
+ *
+ * Code for replacing ftrace calls with jumps.
+ *
+ * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
+ *
+ * Thanks goes to Ingo Molnar, for suggesting the idea.
+ * Mathieu Desnoyers, for suggesting postponing the modifications.
+ * Arjan van de Ven, for keeping me straight, and explaining to me
+ * the dangers of modifying code on the run.
+ */
+#include <linux/uaccess.h>
+#include <linux/ftrace.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <asm/ftrace.h>
+#include <asm/cacheflush.h>
+
+static unsigned char ftrace_nop[] = {
+       0x09, 0x00,             /* nop */
+       0x09, 0x00,             /* nop */
+};
+
+static unsigned char ftrace_replaced_code[MCOUNT_INSN_SIZE];
+
+unsigned char *ftrace_nop_replace(void)
+{
+       return ftrace_nop;
+}
+
+static int is_sh_nop(unsigned char *ip)
+{
+       return strncmp(ip, ftrace_nop, sizeof(ftrace_nop));
+}
+
+unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
+{
+       /* Place the address in the memory table. */
+       if (addr == CALLER_ADDR)
+               __raw_writel(addr + MCOUNT_INSN_OFFSET, ftrace_replaced_code);
+       else
+               __raw_writel(addr, ftrace_replaced_code);
+
+       /*
+        * No locking needed, this must be called via kstop_machine
+        * which in essence is like running on a uniprocessor machine.
+        */
+       return ftrace_replaced_code;
+}
+
+int ftrace_modify_code(unsigned long ip, unsigned char *old_code,
+                      unsigned char *new_code)
+{
+       unsigned char replaced[MCOUNT_INSN_SIZE];
+
+       /*
+        * Note: Due to modules and __init, code can
+        *  disappear and change, we need to protect against faulting
+        *  as well as code changing. We do this by using the
+        *  probe_kernel_* functions.
+        *
+        * No real locking needed, this code is run through
+        * kstop_machine, or before SMP starts.
+        */
+
+       /*
+        * If we're trying to nop out a call to a function, we instead
+        * place a call to the address after the memory table.
+        */
+       if (is_sh_nop(new_code) == 0)
+               __raw_writel(ip + MCOUNT_INSN_SIZE, (unsigned long)new_code);
+
+       /* read the text we want to modify */
+       if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE))
+               return -EFAULT;
+
+       /* Make sure it is what we expect it to be */
+       if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0)
+               return -EINVAL;
+
+       /* replace the text with the new text */
+       if (probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE))
+               return -EPERM;
+
+       flush_icache_range(ip, ip + MCOUNT_INSN_SIZE);
+
+       return 0;
+}
+
+int ftrace_update_ftrace_func(ftrace_func_t func)
+{
+       unsigned long ip = (unsigned long)(&ftrace_call);
+       unsigned char old[MCOUNT_INSN_SIZE], *new;
+
+       memcpy(old, (unsigned char *)(ip + MCOUNT_INSN_OFFSET), MCOUNT_INSN_SIZE);
+       new = ftrace_call_replace(ip, (unsigned long)func);
+
+       return ftrace_modify_code(ip + MCOUNT_INSN_OFFSET, old, new);
+}
+
+int ftrace_make_nop(struct module *mod,
+                   struct dyn_ftrace *rec, unsigned long addr)
+{
+       unsigned char *new, *old;
+       unsigned long ip = rec->ip;
+
+       old = ftrace_call_replace(ip, addr);
+       new = ftrace_nop_replace();
+
+       return ftrace_modify_code(rec->ip, old, new);
+}
+
+int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
+{
+       unsigned char *new, *old;
+       unsigned long ip = rec->ip;
+
+       old = ftrace_nop_replace();
+       new = ftrace_call_replace(ip, addr);
+
+       return ftrace_modify_code(rec->ip, old, new);
+}
+
+int __init ftrace_dyn_arch_init(void *data)
+{
+       /* The return code is retured via data */
+       __raw_writel(0, (unsigned long)data);
+
+       return 0;
+}
index ae0a382..788605f 100644 (file)
@@ -80,8 +80,14 @@ ENTRY(_stext)
        mov.l   7f, r0
        ldc     r0, r7_bank     ! ... and initial thread_info
 #endif
-       
-       !                       Clear BSS area
+
+#ifndef CONFIG_SH_NO_BSS_INIT
+       /*
+        * Don't clear BSS if running on slow platforms such as an RTL simulation,
+        * remote memory via SHdebug link, etc.  For these the memory can be guaranteed
+        * to be all zero on boot anyway.
+        */
+                               ! Clear BSS area
 #ifdef CONFIG_SMP      
        mov.l   3f, r0
        cmp/eq  #0, r0          ! skip clear if set to zero
@@ -97,6 +103,8 @@ ENTRY(_stext)
         mov.l  r0,@-r2
 
 10:            
+#endif
+
        !                       Additional CPU initialization
        mov.l   6f, r0
        jsr     @r0
diff --git a/arch/sh/kernel/idle.c b/arch/sh/kernel/idle.c
new file mode 100644 (file)
index 0000000..fe59ccf
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * The idle loop for all SuperH platforms.
+ *
+ *  Copyright (C) 2002 - 2008  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/pm.h>
+#include <linux/tick.h>
+#include <linux/preempt.h>
+#include <linux/thread_info.h>
+#include <linux/irqflags.h>
+#include <asm/pgalloc.h>
+#include <asm/system.h>
+#include <asm/atomic.h>
+
+static int hlt_counter;
+void (*pm_idle)(void);
+void (*pm_power_off)(void);
+EXPORT_SYMBOL(pm_power_off);
+
+static int __init nohlt_setup(char *__unused)
+{
+       hlt_counter = 1;
+       return 1;
+}
+__setup("nohlt", nohlt_setup);
+
+static int __init hlt_setup(char *__unused)
+{
+       hlt_counter = 0;
+       return 1;
+}
+__setup("hlt", hlt_setup);
+
+static void default_idle(void)
+{
+       if (!hlt_counter) {
+               clear_thread_flag(TIF_POLLING_NRFLAG);
+               smp_mb__after_clear_bit();
+               set_bl_bit();
+               stop_critical_timings();
+
+               while (!need_resched())
+                       cpu_sleep();
+
+               start_critical_timings();
+               clear_bl_bit();
+               set_thread_flag(TIF_POLLING_NRFLAG);
+       } else
+               while (!need_resched())
+                       cpu_relax();
+}
+
+void cpu_idle(void)
+{
+       set_thread_flag(TIF_POLLING_NRFLAG);
+
+       /* endless idle loop with no priority at all */
+       while (1) {
+               void (*idle)(void) = pm_idle;
+
+               if (!idle)
+                       idle = default_idle;
+
+               tick_nohz_stop_sched_tick(1);
+               while (!need_resched())
+                       idle();
+               tick_nohz_restart_sched_tick();
+
+               preempt_enable_no_resched();
+               schedule();
+               preempt_disable();
+               check_pgt_cache();
+       }
+}
diff --git a/arch/sh/kernel/kgdb.c b/arch/sh/kernel/kgdb.c
new file mode 100644 (file)
index 0000000..7c747e7
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * SuperH KGDB support
+ *
+ * Copyright (C) 2008  Paul Mundt
+ *
+ * Single stepping taken from the old stub by Henry Bell and Jeremy Siegel.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kgdb.h>
+#include <linux/kdebug.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <asm/cacheflush.h>
+
+char in_nmi = 0;       /* Set during NMI to prevent re-entry */
+
+/* Macros for single step instruction identification */
+#define OPCODE_BT(op)          (((op) & 0xff00) == 0x8900)
+#define OPCODE_BF(op)          (((op) & 0xff00) == 0x8b00)
+#define OPCODE_BTF_DISP(op)    (((op) & 0x80) ? (((op) | 0xffffff80) << 1) : \
+                                (((op) & 0x7f ) << 1))
+#define OPCODE_BFS(op)         (((op) & 0xff00) == 0x8f00)
+#define OPCODE_BTS(op)         (((op) & 0xff00) == 0x8d00)
+#define OPCODE_BRA(op)         (((op) & 0xf000) == 0xa000)
+#define OPCODE_BRA_DISP(op)    (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \
+                                (((op) & 0x7ff) << 1))
+#define OPCODE_BRAF(op)                (((op) & 0xf0ff) == 0x0023)
+#define OPCODE_BRAF_REG(op)    (((op) & 0x0f00) >> 8)
+#define OPCODE_BSR(op)         (((op) & 0xf000) == 0xb000)
+#define OPCODE_BSR_DISP(op)    (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \
+                                (((op) & 0x7ff) << 1))
+#define OPCODE_BSRF(op)                (((op) & 0xf0ff) == 0x0003)
+#define OPCODE_BSRF_REG(op)    (((op) >> 8) & 0xf)
+#define OPCODE_JMP(op)         (((op) & 0xf0ff) == 0x402b)
+#define OPCODE_JMP_REG(op)     (((op) >> 8) & 0xf)
+#define OPCODE_JSR(op)         (((op) & 0xf0ff) == 0x400b)
+#define OPCODE_JSR_REG(op)     (((op) >> 8) & 0xf)
+#define OPCODE_RTS(op)         ((op) == 0xb)
+#define OPCODE_RTE(op)         ((op) == 0x2b)
+
+#define SR_T_BIT_MASK           0x1
+#define STEP_OPCODE             0xc33d
+
+/* Calculate the new address for after a step */
+static short *get_step_address(struct pt_regs *linux_regs)
+{
+       opcode_t op = __raw_readw(linux_regs->pc);
+       long addr;
+
+       /* BT */
+       if (OPCODE_BT(op)) {
+               if (linux_regs->sr & SR_T_BIT_MASK)
+                       addr = linux_regs->pc + 4 + OPCODE_BTF_DISP(op);
+               else
+                       addr = linux_regs->pc + 2;
+       }
+
+       /* BTS */
+       else if (OPCODE_BTS(op)) {
+               if (linux_regs->sr & SR_T_BIT_MASK)
+                       addr = linux_regs->pc + 4 + OPCODE_BTF_DISP(op);
+               else
+                       addr = linux_regs->pc + 4;      /* Not in delay slot */
+       }
+
+       /* BF */
+       else if (OPCODE_BF(op)) {
+               if (!(linux_regs->sr & SR_T_BIT_MASK))
+                       addr = linux_regs->pc + 4 + OPCODE_BTF_DISP(op);
+               else
+                       addr = linux_regs->pc + 2;
+       }
+
+       /* BFS */
+       else if (OPCODE_BFS(op)) {
+               if (!(linux_regs->sr & SR_T_BIT_MASK))
+                       addr = linux_regs->pc + 4 + OPCODE_BTF_DISP(op);
+               else
+                       addr = linux_regs->pc + 4;      /* Not in delay slot */
+       }
+
+       /* BRA */
+       else if (OPCODE_BRA(op))
+               addr = linux_regs->pc + 4 + OPCODE_BRA_DISP(op);
+
+       /* BRAF */
+       else if (OPCODE_BRAF(op))
+               addr = linux_regs->pc + 4
+                   + linux_regs->regs[OPCODE_BRAF_REG(op)];
+
+       /* BSR */
+       else if (OPCODE_BSR(op))
+               addr = linux_regs->pc + 4 + OPCODE_BSR_DISP(op);
+
+       /* BSRF */
+       else if (OPCODE_BSRF(op))
+               addr = linux_regs->pc + 4
+                   + linux_regs->regs[OPCODE_BSRF_REG(op)];
+
+       /* JMP */
+       else if (OPCODE_JMP(op))
+               addr = linux_regs->regs[OPCODE_JMP_REG(op)];
+
+       /* JSR */
+       else if (OPCODE_JSR(op))
+               addr = linux_regs->regs[OPCODE_JSR_REG(op)];
+
+       /* RTS */
+       else if (OPCODE_RTS(op))
+               addr = linux_regs->pr;
+
+       /* RTE */
+       else if (OPCODE_RTE(op))
+               addr = linux_regs->regs[15];
+
+       /* Other */
+       else
+               addr = linux_regs->pc + instruction_size(op);
+
+       flush_icache_range(addr, addr + instruction_size(op));
+       return (short *)addr;
+}
+
+/*
+ * Replace the instruction immediately after the current instruction
+ * (i.e. next in the expected flow of control) with a trap instruction,
+ * so that returning will cause only a single instruction to be executed.
+ * Note that this model is slightly broken for instructions with delay
+ * slots (e.g. B[TF]S, BSR, BRA etc), where both the branch and the
+ * instruction in the delay slot will be executed.
+ */
+
+static unsigned long stepped_address;
+static opcode_t stepped_opcode;
+
+static void do_single_step(struct pt_regs *linux_regs)
+{
+       /* Determine where the target instruction will send us to */
+       unsigned short *addr = get_step_address(linux_regs);
+
+       stepped_address = (int)addr;
+
+       /* Replace it */
+       stepped_opcode = __raw_readw((long)addr);
+       *addr = STEP_OPCODE;
+
+       /* Flush and return */
+       flush_icache_range((long)addr, (long)addr +
+                          instruction_size(stepped_opcode));
+}
+
+/* Undo a single step */
+static void undo_single_step(struct pt_regs *linux_regs)
+{
+       /* If we have stepped, put back the old instruction */
+       /* Use stepped_address in case we stopped elsewhere */
+       if (stepped_opcode != 0) {
+               __raw_writew(stepped_opcode, stepped_address);
+               flush_icache_range(stepped_address, stepped_address + 2);
+       }
+
+       stepped_opcode = 0;
+}
+
+void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+       int i;
+
+       for (i = 0; i < 16; i++)
+               gdb_regs[GDB_R0 + i] = regs->regs[i];
+
+       gdb_regs[GDB_PC] = regs->pc;
+       gdb_regs[GDB_PR] = regs->pr;
+       gdb_regs[GDB_SR] = regs->sr;
+       gdb_regs[GDB_GBR] = regs->gbr;
+       gdb_regs[GDB_MACH] = regs->mach;
+       gdb_regs[GDB_MACL] = regs->macl;
+
+       __asm__ __volatile__ ("stc vbr, %0" : "=r" (gdb_regs[GDB_VBR]));
+}
+
+void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+       int i;
+
+       for (i = 0; i < 16; i++)
+               regs->regs[GDB_R0 + i] = gdb_regs[GDB_R0 + i];
+
+       regs->pc = gdb_regs[GDB_PC];
+       regs->pr = gdb_regs[GDB_PR];
+       regs->sr = gdb_regs[GDB_SR];
+       regs->gbr = gdb_regs[GDB_GBR];
+       regs->mach = gdb_regs[GDB_MACH];
+       regs->macl = gdb_regs[GDB_MACL];
+
+       __asm__ __volatile__ ("ldc %0, vbr" : : "r" (gdb_regs[GDB_VBR]));
+}
+
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+       gdb_regs[GDB_R15] = p->thread.sp;
+       gdb_regs[GDB_PC] = p->thread.pc;
+}
+
+int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
+                              char *remcomInBuffer, char *remcomOutBuffer,
+                              struct pt_regs *linux_regs)
+{
+       unsigned long addr;
+       char *ptr;
+
+       /* Undo any stepping we may have done */
+       undo_single_step(linux_regs);
+
+       switch (remcomInBuffer[0]) {
+       case 'c':
+       case 's':
+               /* try to read optional parameter, pc unchanged if no parm */
+               ptr = &remcomInBuffer[1];
+               if (kgdb_hex2long(&ptr, &addr))
+                       linux_regs->pc = addr;
+       case 'D':
+       case 'k':
+               atomic_set(&kgdb_cpu_doing_single_step, -1);
+
+               if (remcomInBuffer[0] == 's') {
+                       do_single_step(linux_regs);
+                       kgdb_single_step = 1;
+
+                       atomic_set(&kgdb_cpu_doing_single_step,
+                                  raw_smp_processor_id());
+               }
+
+               return 0;
+       }
+
+       /* this means that we do not want to exit from the handler: */
+       return -1;
+}
+
+/*
+ * The primary entry points for the kgdb debug trap table entries.
+ */
+BUILD_TRAP_HANDLER(singlestep)
+{
+       unsigned long flags;
+       TRAP_HANDLER_DECL;
+
+       local_irq_save(flags);
+       regs->pc -= instruction_size(__raw_readw(regs->pc - 4));
+       kgdb_handle_exception(vec >> 2, SIGTRAP, 0, regs);
+       local_irq_restore(flags);
+}
+
+
+BUILD_TRAP_HANDLER(breakpoint)
+{
+       unsigned long flags;
+       TRAP_HANDLER_DECL;
+
+       local_irq_save(flags);
+       kgdb_handle_exception(vec >> 2, SIGTRAP, 0, regs);
+       local_irq_restore(flags);
+}
+
+int kgdb_arch_init(void)
+{
+       return 0;
+}
+
+void kgdb_arch_exit(void)
+{
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+       /* Breakpoint instruction: trapa #0x3c */
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+       .gdb_bpt_instr          = { 0x3c, 0xc3 },
+#else
+       .gdb_bpt_instr          = { 0xc3, 0x3c },
+#endif
+};
diff --git a/arch/sh/kernel/kgdb_jmp.S b/arch/sh/kernel/kgdb_jmp.S
deleted file mode 100644 (file)
index 339bb1d..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#include <linux/linkage.h>
-
-ENTRY(setjmp)
-       add     #(9*4), r4
-       sts.l   pr, @-r4
-       mov.l   r15, @-r4
-       mov.l   r14, @-r4
-       mov.l   r13, @-r4
-       mov.l   r12, @-r4
-       mov.l   r11, @-r4
-       mov.l   r10, @-r4
-       mov.l   r9, @-r4
-       mov.l   r8, @-r4
-       rts
-        mov    #0, r0
-
-ENTRY(longjmp)
-       mov.l   @r4+, r8
-       mov.l   @r4+, r9
-       mov.l   @r4+, r10
-       mov.l   @r4+, r11
-       mov.l   @r4+, r12
-       mov.l   @r4+, r13
-       mov.l   @r4+, r14
-       mov.l   @r4+, r15
-       lds.l   @r4+, pr
-       mov     r5, r0
-       tst     r0, r0
-       bf      1f
-       mov     #1, r0  ! in case val==0
-1:     rts
-        nop
-
diff --git a/arch/sh/kernel/kgdb_stub.c b/arch/sh/kernel/kgdb_stub.c
deleted file mode 100644 (file)
index bf8ac4c..0000000
+++ /dev/null
@@ -1,1052 +0,0 @@
-/*
- * May be copied or modified under the terms of the GNU General Public
- * License.  See linux/COPYING for more information.
- *
- * Contains extracts from code by Glenn Engel, Jim Kingdon,
- * David Grothe <dave@gcom.com>, Tigran Aivazian <tigran@sco.com>,
- * Amit S. Kale <akale@veritas.com>,  William Gatliff <bgat@open-widgets.com>,
- * Ben Lee, Steve Chamberlain and Benoit Miller <fulg@iname.com>.
- *
- * This version by Henry Bell <henry.bell@st.com>
- * Minor modifications by Jeremy Siegel <jsiegel@mvista.com>
- *
- * Contains low-level support for remote debug using GDB.
- *
- * To enable debugger support, two things need to happen. A call to
- * set_debug_traps() is necessary in order to allow any breakpoints
- * or error conditions to be properly intercepted and reported to gdb.
- * A breakpoint also needs to be generated to begin communication.  This
- * is most easily accomplished by a call to breakpoint() which does
- * a trapa if the initialisation phase has been successfully completed.
- *
- * In this case, set_debug_traps() is not used to "take over" exceptions;
- * other kernel code is modified instead to enter the kgdb functions here
- * when appropriate (see entry.S for breakpoint traps and NMI interrupts,
- * see traps.c for kernel error exceptions).
- *
- * The following gdb commands are supported:
- *
- *    Command       Function                               Return value
- *
- *    g             return the value of the CPU registers  hex data or ENN
- *    G             set the value of the CPU registers     OK or ENN
- *
- *    mAA..AA,LLLL  Read LLLL bytes at address AA..AA      hex data or ENN
- *    MAA..AA,LLLL: Write LLLL bytes at address AA.AA      OK or ENN
- *    XAA..AA,LLLL: Same, but data is binary (not hex)     OK or ENN
- *
- *    c             Resume at current address              SNN   ( signal NN)
- *    cAA..AA       Continue at address AA..AA             SNN
- *    CNN;          Resume at current address with signal  SNN
- *    CNN;AA..AA    Resume at address AA..AA with signal   SNN
- *
- *    s             Step one instruction                   SNN
- *    sAA..AA       Step one instruction from AA..AA       SNN
- *    SNN;          Step one instruction with signal       SNN
- *    SNNAA..AA     Step one instruction from AA..AA w/NN  SNN
- *
- *    k             kill (Detach GDB)
- *
- *    d             Toggle debug flag
- *    D             Detach GDB
- *
- *    Hct           Set thread t for operations,           OK or ENN
- *                  c = 'c' (step, cont), c = 'g' (other
- *                  operations)
- *
- *    qC            Query current thread ID                QCpid
- *    qfThreadInfo  Get list of current threads (first)    m<id>
- *    qsThreadInfo   "    "  "     "      "   (subsequent)
- *    qOffsets      Get section offsets                  Text=x;Data=y;Bss=z
- *
- *    TXX           Find if thread XX is alive             OK or ENN
- *    ?             What was the last sigval ?             SNN   (signal NN)
- *    O             Output to GDB console
- *
- * Remote communication protocol.
- *
- *    A debug packet whose contents are <data> is encapsulated for
- *    transmission in the form:
- *
- *       $ <data> # CSUM1 CSUM2
- *
- *       <data> must be ASCII alphanumeric and cannot include characters
- *       '$' or '#'.  If <data> starts with two characters followed by
- *       ':', then the existing stubs interpret this as a sequence number.
- *
- *       CSUM1 and CSUM2 are ascii hex representation of an 8-bit
- *       checksum of <data>, the most significant nibble is sent first.
- *       the hex digits 0-9,a-f are used.
- *
- *    Receiver responds with:
- *
- *       +       - if CSUM is correct and ready for next packet
- *       -       - if CSUM is incorrect
- *
- * Responses can be run-length encoded to save space.  A '*' means that
- * the next character is an ASCII encoding giving a repeat count which
- * stands for that many repetitions of the character preceding the '*'.
- * The encoding is n+29, yielding a printable character where n >=3
- * (which is where RLE starts to win).  Don't use an n > 126.
- *
- * So "0* " means the same as "0000".
- */
-
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/smp.h>
-#include <linux/spinlock.h>
-#include <linux/delay.h>
-#include <linux/linkage.h>
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/sysrq.h>
-#include <linux/module.h>
-#include <asm/system.h>
-#include <asm/cacheflush.h>
-#include <asm/current.h>
-#include <asm/signal.h>
-#include <asm/pgtable.h>
-#include <asm/ptrace.h>
-#include <asm/kgdb.h>
-#include <asm/io.h>
-
-/* Function pointers for linkage */
-kgdb_debug_hook_t *kgdb_debug_hook;
-kgdb_bus_error_hook_t *kgdb_bus_err_hook;
-
-int (*kgdb_getchar)(void);
-EXPORT_SYMBOL_GPL(kgdb_getchar);
-void (*kgdb_putchar)(int);
-EXPORT_SYMBOL_GPL(kgdb_putchar);
-
-static void put_debug_char(int c)
-{
-       if (!kgdb_putchar)
-               return;
-       (*kgdb_putchar)(c);
-}
-static int get_debug_char(void)
-{
-       if (!kgdb_getchar)
-               return -1;
-       return (*kgdb_getchar)();
-}
-
-/* Num chars in in/out bound buffers, register packets need NUMREGBYTES * 2 */
-#define BUFMAX 1024
-#define NUMREGBYTES (MAXREG*4)
-#define OUTBUFMAX (NUMREGBYTES*2+512)
-
-enum {
-       R0 = 0, R1,  R2,  R3,   R4,   R5,  R6, R7,
-       R8, R9, R10, R11, R12,  R13,  R14, R15,
-       PC, PR, GBR, VBR, MACH, MACL, SR,
-       /*  */
-       MAXREG
-};
-
-static unsigned int registers[MAXREG];
-struct kgdb_regs trap_registers;
-
-char kgdb_in_gdb_mode;
-char in_nmi;                   /* Set during NMI to prevent reentry */
-int kgdb_nofault;              /* Boolean to ignore bus errs (i.e. in GDB) */
-
-/* Default values for SCI (can override via kernel args in setup.c) */
-#ifndef CONFIG_KGDB_DEFPORT
-#define CONFIG_KGDB_DEFPORT 1
-#endif
-
-#ifndef CONFIG_KGDB_DEFBAUD
-#define CONFIG_KGDB_DEFBAUD 115200
-#endif
-
-#if defined(CONFIG_KGDB_DEFPARITY_E)
-#define CONFIG_KGDB_DEFPARITY 'E'
-#elif defined(CONFIG_KGDB_DEFPARITY_O)
-#define CONFIG_KGDB_DEFPARITY 'O'
-#else /* CONFIG_KGDB_DEFPARITY_N */
-#define CONFIG_KGDB_DEFPARITY 'N'
-#endif
-
-#ifdef CONFIG_KGDB_DEFBITS_7
-#define CONFIG_KGDB_DEFBITS '7'
-#else /* CONFIG_KGDB_DEFBITS_8 */
-#define CONFIG_KGDB_DEFBITS '8'
-#endif
-
-/* SCI/UART settings, used in kgdb_console_setup() */
-int  kgdb_portnum = CONFIG_KGDB_DEFPORT;
-EXPORT_SYMBOL_GPL(kgdb_portnum);
-int  kgdb_baud = CONFIG_KGDB_DEFBAUD;
-EXPORT_SYMBOL_GPL(kgdb_baud);
-char kgdb_parity = CONFIG_KGDB_DEFPARITY;
-EXPORT_SYMBOL_GPL(kgdb_parity);
-char kgdb_bits = CONFIG_KGDB_DEFBITS;
-EXPORT_SYMBOL_GPL(kgdb_bits);
-
-/* Jump buffer for setjmp/longjmp */
-static jmp_buf rem_com_env;
-
-/* TRA differs sh3/4 */
-#if defined(CONFIG_CPU_SH3)
-#define TRA 0xffffffd0
-#elif defined(CONFIG_CPU_SH4)
-#define TRA 0xff000020
-#endif
-
-/* Macros for single step instruction identification */
-#define OPCODE_BT(op)         (((op) & 0xff00) == 0x8900)
-#define OPCODE_BF(op)         (((op) & 0xff00) == 0x8b00)
-#define OPCODE_BTF_DISP(op)   (((op) & 0x80) ? (((op) | 0xffffff80) << 1) : \
-                             (((op) & 0x7f ) << 1))
-#define OPCODE_BFS(op)        (((op) & 0xff00) == 0x8f00)
-#define OPCODE_BTS(op)        (((op) & 0xff00) == 0x8d00)
-#define OPCODE_BRA(op)        (((op) & 0xf000) == 0xa000)
-#define OPCODE_BRA_DISP(op)   (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \
-                             (((op) & 0x7ff) << 1))
-#define OPCODE_BRAF(op)       (((op) & 0xf0ff) == 0x0023)
-#define OPCODE_BRAF_REG(op)   (((op) & 0x0f00) >> 8)
-#define OPCODE_BSR(op)        (((op) & 0xf000) == 0xb000)
-#define OPCODE_BSR_DISP(op)   (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \
-                             (((op) & 0x7ff) << 1))
-#define OPCODE_BSRF(op)       (((op) & 0xf0ff) == 0x0003)
-#define OPCODE_BSRF_REG(op)   (((op) >> 8) & 0xf)
-#define OPCODE_JMP(op)        (((op) & 0xf0ff) == 0x402b)
-#define OPCODE_JMP_REG(op)    (((op) >> 8) & 0xf)
-#define OPCODE_JSR(op)        (((op) & 0xf0ff) == 0x400b)
-#define OPCODE_JSR_REG(op)    (((op) >> 8) & 0xf)
-#define OPCODE_RTS(op)        ((op) == 0xb)
-#define OPCODE_RTE(op)        ((op) == 0x2b)
-
-#define SR_T_BIT_MASK           0x1
-#define STEP_OPCODE             0xc320
-#define BIOS_CALL_TRAP          0x3f
-
-/* Exception codes as per SH-4 core manual */
-#define ADDRESS_ERROR_LOAD_VEC   7
-#define ADDRESS_ERROR_STORE_VEC  8
-#define TRAP_VEC                 11
-#define INVALID_INSN_VEC         12
-#define INVALID_SLOT_VEC         13
-#define NMI_VEC                  14
-#define USER_BREAK_VEC           15
-#define SERIAL_BREAK_VEC         58
-
-/* Misc static */
-static int stepped_address;
-static short stepped_opcode;
-static char in_buffer[BUFMAX];
-static char out_buffer[OUTBUFMAX];
-
-static void kgdb_to_gdb(const char *s);
-
-/* Convert ch to hex */
-static int hex(const char ch)
-{
-       if ((ch >= 'a') && (ch <= 'f'))
-               return (ch - 'a' + 10);
-       if ((ch >= '0') && (ch <= '9'))
-               return (ch - '0');
-       if ((ch >= 'A') && (ch <= 'F'))
-               return (ch - 'A' + 10);
-       return (-1);
-}
-
-/* Convert the memory pointed to by mem into hex, placing result in buf.
-   Returns a pointer to the last char put in buf (null) */
-static char *mem_to_hex(const char *mem, char *buf, const int count)
-{
-       int i;
-       int ch;
-       unsigned short s_val;
-       unsigned long l_val;
-
-       /* Check for 16 or 32 */
-       if (count == 2 && ((long) mem & 1) == 0) {
-               s_val = *(unsigned short *) mem;
-               mem = (char *) &s_val;
-       } else if (count == 4 && ((long) mem & 3) == 0) {
-               l_val = *(unsigned long *) mem;
-               mem = (char *) &l_val;
-       }
-       for (i = 0; i < count; i++) {
-               ch = *mem++;
-               buf = pack_hex_byte(buf, ch);
-       }
-       *buf = 0;
-       return (buf);
-}
-
-/* Convert the hex array pointed to by buf into binary, to be placed in mem.
-   Return a pointer to the character after the last byte written */
-static char *hex_to_mem(const char *buf, char *mem, const int count)
-{
-       int i;
-       unsigned char ch;
-
-       for (i = 0; i < count; i++) {
-               ch = hex(*buf++) << 4;
-               ch = ch + hex(*buf++);
-               *mem++ = ch;
-       }
-       return (mem);
-}
-
-/* While finding valid hex chars, convert to an integer, then return it */
-static int hex_to_int(char **ptr, int *int_value)
-{
-       int num_chars = 0;
-       int hex_value;
-
-       *int_value = 0;
-
-       while (**ptr) {
-               hex_value = hex(**ptr);
-               if (hex_value >= 0) {
-                       *int_value = (*int_value << 4) | hex_value;
-                       num_chars++;
-               } else
-                       break;
-               (*ptr)++;
-       }
-       return num_chars;
-}
-
-/*  Copy the binary array pointed to by buf into mem.  Fix $, #,
-    and 0x7d escaped with 0x7d.  Return a pointer to the character
-    after the last byte written. */
-static char *ebin_to_mem(const char *buf, char *mem, int count)
-{
-       for (; count > 0; count--, buf++) {
-               if (*buf == 0x7d)
-                       *mem++ = *(++buf) ^ 0x20;
-               else
-                       *mem++ = *buf;
-       }
-       return mem;
-}
-
-/* Scan for the start char '$', read the packet and check the checksum */
-static void get_packet(char *buffer, int buflen)
-{
-       unsigned char checksum;
-       unsigned char xmitcsum;
-       int i;
-       int count;
-       char ch;
-
-       do {
-               /* Ignore everything until the start character */
-               while ((ch = get_debug_char()) != '$');
-
-               checksum = 0;
-               xmitcsum = -1;
-               count = 0;
-
-               /* Now, read until a # or end of buffer is found */
-               while (count < (buflen - 1)) {
-                       ch = get_debug_char();
-
-                       if (ch == '#')
-                               break;
-
-                       checksum = checksum + ch;
-                       buffer[count] = ch;
-                       count = count + 1;
-               }
-
-               buffer[count] = 0;
-
-               /* Continue to read checksum following # */
-               if (ch == '#') {
-                       xmitcsum = hex(get_debug_char()) << 4;
-                       xmitcsum += hex(get_debug_char());
-
-                       /* Checksum */
-                       if (checksum != xmitcsum)
-                               put_debug_char('-');    /* Failed checksum */
-                       else {
-                               /* Ack successful transfer */
-                               put_debug_char('+');
-
-                               /* If a sequence char is present, reply
-                                  the sequence ID */
-                               if (buffer[2] == ':') {
-                                       put_debug_char(buffer[0]);
-                                       put_debug_char(buffer[1]);
-
-                                       /* Remove sequence chars from buffer */
-                                       count = strlen(buffer);
-                                       for (i = 3; i <= count; i++)
-                                               buffer[i - 3] = buffer[i];
-                               }
-                       }
-               }
-       }
-       while (checksum != xmitcsum);   /* Keep trying while we fail */
-}
-
-/* Send the packet in the buffer with run-length encoding */
-static void put_packet(char *buffer)
-{
-       int checksum;
-       char *src;
-       int runlen;
-       int encode;
-
-       do {
-               src = buffer;
-               put_debug_char('$');
-               checksum = 0;
-
-               /* Continue while we still have chars left */
-               while (*src) {
-                       /* Check for runs up to 99 chars long */
-                       for (runlen = 1; runlen < 99; runlen++) {
-                               if (src[0] != src[runlen])
-                                       break;
-                       }
-
-                       if (runlen > 3) {
-                               /* Got a useful amount, send encoding */
-                               encode = runlen + ' ' - 4;
-                               put_debug_char(*src);   checksum += *src;
-                               put_debug_char('*');    checksum += '*';
-                               put_debug_char(encode); checksum += encode;
-                               src += runlen;
-                       } else {
-                               /* Otherwise just send the current char */
-                               put_debug_char(*src);   checksum += *src;
-                               src += 1;
-                       }
-               }
-
-               /* '#' Separator, put high and low components of checksum */
-               put_debug_char('#');
-               put_debug_char(hex_asc_hi(checksum));
-               put_debug_char(hex_asc_lo(checksum));
-       }
-       while ((get_debug_char()) != '+');      /* While no ack */
-}
-
-/* A bus error has occurred - perform a longjmp to return execution and
-   allow handling of the error */
-static void kgdb_handle_bus_error(void)
-{
-       longjmp(rem_com_env, 1);
-}
-
-/* Translate SH-3/4 exception numbers to unix-like signal values */
-static int compute_signal(const int excep_code)
-{
-       int sigval;
-
-       switch (excep_code) {
-
-       case INVALID_INSN_VEC:
-       case INVALID_SLOT_VEC:
-               sigval = SIGILL;
-               break;
-       case ADDRESS_ERROR_LOAD_VEC:
-       case ADDRESS_ERROR_STORE_VEC:
-               sigval = SIGSEGV;
-               break;
-
-       case SERIAL_BREAK_VEC:
-       case NMI_VEC:
-               sigval = SIGINT;
-               break;
-
-       case USER_BREAK_VEC:
-       case TRAP_VEC:
-               sigval = SIGTRAP;
-               break;
-
-       default:
-               sigval = SIGBUS;        /* "software generated" */
-               break;
-       }
-
-       return (sigval);
-}
-
-/* Make a local copy of the registers passed into the handler (bletch) */
-static void kgdb_regs_to_gdb_regs(const struct kgdb_regs *regs,
-                                 int *gdb_regs)
-{
-       gdb_regs[R0] = regs->regs[R0];
-       gdb_regs[R1] = regs->regs[R1];
-       gdb_regs[R2] = regs->regs[R2];
-       gdb_regs[R3] = regs->regs[R3];
-       gdb_regs[R4] = regs->regs[R4];
-       gdb_regs[R5] = regs->regs[R5];
-       gdb_regs[R6] = regs->regs[R6];
-       gdb_regs[R7] = regs->regs[R7];
-       gdb_regs[R8] = regs->regs[R8];
-       gdb_regs[R9] = regs->regs[R9];
-       gdb_regs[R10] = regs->regs[R10];
-       gdb_regs[R11] = regs->regs[R11];
-       gdb_regs[R12] = regs->regs[R12];
-       gdb_regs[R13] = regs->regs[R13];
-       gdb_regs[R14] = regs->regs[R14];
-       gdb_regs[R15] = regs->regs[R15];
-       gdb_regs[PC] = regs->pc;
-       gdb_regs[PR] = regs->pr;
-       gdb_regs[GBR] = regs->gbr;
-       gdb_regs[MACH] = regs->mach;
-       gdb_regs[MACL] = regs->macl;
-       gdb_regs[SR] = regs->sr;
-       gdb_regs[VBR] = regs->vbr;
-}
-
-/* Copy local gdb registers back to kgdb regs, for later copy to kernel */
-static void gdb_regs_to_kgdb_regs(const int *gdb_regs,
-                                 struct kgdb_regs *regs)
-{
-       regs->regs[R0] = gdb_regs[R0];
-       regs->regs[R1] = gdb_regs[R1];
-       regs->regs[R2] = gdb_regs[R2];
-       regs->regs[R3] = gdb_regs[R3];
-       regs->regs[R4] = gdb_regs[R4];
-       regs->regs[R5] = gdb_regs[R5];
-       regs->regs[R6] = gdb_regs[R6];
-       regs->regs[R7] = gdb_regs[R7];
-       regs->regs[R8] = gdb_regs[R8];
-       regs->regs[R9] = gdb_regs[R9];
-       regs->regs[R10] = gdb_regs[R10];
-       regs->regs[R11] = gdb_regs[R11];
-       regs->regs[R12] = gdb_regs[R12];
-       regs->regs[R13] = gdb_regs[R13];
-       regs->regs[R14] = gdb_regs[R14];
-       regs->regs[R15] = gdb_regs[R15];
-       regs->pc = gdb_regs[PC];
-       regs->pr = gdb_regs[PR];
-       regs->gbr = gdb_regs[GBR];
-       regs->mach = gdb_regs[MACH];
-       regs->macl = gdb_regs[MACL];
-       regs->sr = gdb_regs[SR];
-       regs->vbr = gdb_regs[VBR];
-}
-
-/* Calculate the new address for after a step */
-static short *get_step_address(void)
-{
-       short op = *(short *) trap_registers.pc;
-       long addr;
-
-       /* BT */
-       if (OPCODE_BT(op)) {
-               if (trap_registers.sr & SR_T_BIT_MASK)
-                       addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
-               else
-                       addr = trap_registers.pc + 2;
-       }
-
-       /* BTS */
-       else if (OPCODE_BTS(op)) {
-               if (trap_registers.sr & SR_T_BIT_MASK)
-                       addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
-               else
-                       addr = trap_registers.pc + 4;   /* Not in delay slot */
-       }
-
-       /* BF */
-       else if (OPCODE_BF(op)) {
-               if (!(trap_registers.sr & SR_T_BIT_MASK))
-                       addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
-               else
-                       addr = trap_registers.pc + 2;
-       }
-
-       /* BFS */
-       else if (OPCODE_BFS(op)) {
-               if (!(trap_registers.sr & SR_T_BIT_MASK))
-                       addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
-               else
-                       addr = trap_registers.pc + 4;   /* Not in delay slot */
-       }
-
-       /* BRA */
-       else if (OPCODE_BRA(op))
-               addr = trap_registers.pc + 4 + OPCODE_BRA_DISP(op);
-
-       /* BRAF */
-       else if (OPCODE_BRAF(op))
-               addr = trap_registers.pc + 4
-                   + trap_registers.regs[OPCODE_BRAF_REG(op)];
-
-       /* BSR */
-       else if (OPCODE_BSR(op))
-               addr = trap_registers.pc + 4 + OPCODE_BSR_DISP(op);
-
-       /* BSRF */
-       else if (OPCODE_BSRF(op))
-               addr = trap_registers.pc + 4
-                   + trap_registers.regs[OPCODE_BSRF_REG(op)];
-
-       /* JMP */
-       else if (OPCODE_JMP(op))
-               addr = trap_registers.regs[OPCODE_JMP_REG(op)];
-
-       /* JSR */
-       else if (OPCODE_JSR(op))
-               addr = trap_registers.regs[OPCODE_JSR_REG(op)];
-
-       /* RTS */
-       else if (OPCODE_RTS(op))
-               addr = trap_registers.pr;
-
-       /* RTE */
-       else if (OPCODE_RTE(op))
-               addr = trap_registers.regs[15];
-
-       /* Other */
-       else
-               addr = trap_registers.pc + 2;
-
-       flush_icache_range(addr, addr + 2);
-       return (short *) addr;
-}
-
-/* Set up a single-step.  Replace the instruction immediately after the
-   current instruction (i.e. next in the expected flow of control) with a
-   trap instruction, so that returning will cause only a single instruction
-   to be executed. Note that this model is slightly broken for instructions
-   with delay slots (e.g. B[TF]S, BSR, BRA etc), where both the branch
-   and the instruction in the delay slot will be executed. */
-static void do_single_step(void)
-{
-       unsigned short *addr = 0;
-
-       /* Determine where the target instruction will send us to */
-       addr = get_step_address();
-       stepped_address = (int)addr;
-
-       /* Replace it */
-       stepped_opcode = *(short *)addr;
-       *addr = STEP_OPCODE;
-
-       /* Flush and return */
-       flush_icache_range((long) addr, (long) addr + 2);
-}
-
-/* Undo a single step */
-static void undo_single_step(void)
-{
-       /* If we have stepped, put back the old instruction */
-       /* Use stepped_address in case we stopped elsewhere */
-       if (stepped_opcode != 0) {
-               *(short*)stepped_address = stepped_opcode;
-               flush_icache_range(stepped_address, stepped_address + 2);
-       }
-       stepped_opcode = 0;
-}
-
-/* Send a signal message */
-static void send_signal_msg(const int signum)
-{
-       out_buffer[0] = 'S';
-       out_buffer[1] = hex_asc_hi(signum);
-       out_buffer[2] = hex_asc_lo(signum);
-       out_buffer[3] = 0;
-       put_packet(out_buffer);
-}
-
-/* Reply that all was well */
-static void send_ok_msg(void)
-{
-       strcpy(out_buffer, "OK");
-       put_packet(out_buffer);
-}
-
-/* Reply that an error occurred */
-static void send_err_msg(void)
-{
-       strcpy(out_buffer, "E01");
-       put_packet(out_buffer);
-}
-
-/* Empty message indicates unrecognised command */
-static void send_empty_msg(void)
-{
-       put_packet("");
-}
-
-/* Read memory due to 'm' message */
-static void read_mem_msg(void)
-{
-       char *ptr;
-       int addr;
-       int length;
-
-       /* Jmp, disable bus error handler */
-       if (setjmp(rem_com_env) == 0) {
-
-               kgdb_nofault = 1;
-
-               /* Walk through, have m<addr>,<length> */
-               ptr = &in_buffer[1];
-               if (hex_to_int(&ptr, &addr) && (*ptr++ == ','))
-                       if (hex_to_int(&ptr, &length)) {
-                               ptr = 0;
-                               if (length * 2 > OUTBUFMAX)
-                                       length = OUTBUFMAX / 2;
-                               mem_to_hex((char *) addr, out_buffer, length);
-                       }
-               if (ptr)
-                       send_err_msg();
-               else
-                       put_packet(out_buffer);
-       } else
-               send_err_msg();
-
-       /* Restore bus error handler */
-       kgdb_nofault = 0;
-}
-
-/* Write memory due to 'M' or 'X' message */
-static void write_mem_msg(int binary)
-{
-       char *ptr;
-       int addr;
-       int length;
-
-       if (setjmp(rem_com_env) == 0) {
-
-               kgdb_nofault = 1;
-
-               /* Walk through, have M<addr>,<length>:<data> */
-               ptr = &in_buffer[1];
-               if (hex_to_int(&ptr, &addr) && (*ptr++ == ','))
-                       if (hex_to_int(&ptr, &length) && (*ptr++ == ':')) {
-                               if (binary)
-                                       ebin_to_mem(ptr, (char*)addr, length);
-                               else
-                                       hex_to_mem(ptr, (char*)addr, length);
-                               flush_icache_range(addr, addr + length);
-                               ptr = 0;
-                               send_ok_msg();
-                       }
-               if (ptr)
-                       send_err_msg();
-       } else
-               send_err_msg();
-
-       /* Restore bus error handler */
-       kgdb_nofault = 0;
-}
-
-/* Continue message  */
-static void continue_msg(void)
-{
-       /* Try to read optional parameter, PC unchanged if none */
-       char *ptr = &in_buffer[1];
-       int addr;
-
-       if (hex_to_int(&ptr, &addr))
-               trap_registers.pc = addr;
-}
-
-/* Continue message with signal */
-static void continue_with_sig_msg(void)
-{
-       int signal;
-       char *ptr = &in_buffer[1];
-       int addr;
-
-       /* Report limitation */
-       kgdb_to_gdb("Cannot force signal in kgdb, continuing anyway.\n");
-
-       /* Signal */
-       hex_to_int(&ptr, &signal);
-       if (*ptr == ';')
-               ptr++;
-
-       /* Optional address */
-       if (hex_to_int(&ptr, &addr))
-               trap_registers.pc = addr;
-}
-
-/* Step message */
-static void step_msg(void)
-{
-       continue_msg();
-       do_single_step();
-}
-
-/* Step message with signal */
-static void step_with_sig_msg(void)
-{
-       continue_with_sig_msg();
-       do_single_step();
-}
-
-/* Send register contents */
-static void send_regs_msg(void)
-{
-       kgdb_regs_to_gdb_regs(&trap_registers, registers);
-       mem_to_hex((char *) registers, out_buffer, NUMREGBYTES);
-       put_packet(out_buffer);
-}
-
-/* Set register contents - currently can't set other thread's registers */
-static void set_regs_msg(void)
-{
-       kgdb_regs_to_gdb_regs(&trap_registers, registers);
-       hex_to_mem(&in_buffer[1], (char *) registers, NUMREGBYTES);
-       gdb_regs_to_kgdb_regs(registers, &trap_registers);
-       send_ok_msg();
-}
-
-#ifdef CONFIG_SH_KGDB_CONSOLE
-/*
- * Bring up the ports..
- */
-static int __init kgdb_serial_setup(void)
-{
-       struct console dummy;
-       return kgdb_console_setup(&dummy, 0);
-}
-#else
-#define kgdb_serial_setup()    0
-#endif
-
-/* The command loop, read and act on requests */
-static void kgdb_command_loop(const int excep_code, const int trapa_value)
-{
-       int sigval;
-
-       /* Enter GDB mode (e.g. after detach) */
-       if (!kgdb_in_gdb_mode) {
-               /* Do serial setup, notify user, issue preemptive ack */
-               printk(KERN_NOTICE "KGDB: Waiting for GDB\n");
-               kgdb_in_gdb_mode = 1;
-               put_debug_char('+');
-       }
-
-       /* Reply to host that an exception has occurred */
-       sigval = compute_signal(excep_code);
-       send_signal_msg(sigval);
-
-       /* TRAP_VEC exception indicates a software trap inserted in place of
-          code by GDB so back up PC by one instruction, as this instruction
-          will later be replaced by its original one.  Do NOT do this for
-          trap 0xff, since that indicates a compiled-in breakpoint which
-          will not be replaced (and we would retake the trap forever) */
-       if ((excep_code == TRAP_VEC) && (trapa_value != (0x3c << 2)))
-               trap_registers.pc -= 2;
-
-       /* Undo any stepping we may have done */
-       undo_single_step();
-
-       while (1) {
-               out_buffer[0] = 0;
-               get_packet(in_buffer, BUFMAX);
-
-               /* Examine first char of buffer to see what we need to do */
-               switch (in_buffer[0]) {
-               case '?':       /* Send which signal we've received */
-                       send_signal_msg(sigval);
-                       break;
-
-               case 'g':       /* Return the values of the CPU registers */
-                       send_regs_msg();
-                       break;
-
-               case 'G':       /* Set the value of the CPU registers */
-                       set_regs_msg();
-                       break;
-
-               case 'm':       /* Read LLLL bytes address AA..AA */
-                       read_mem_msg();
-                       break;
-
-               case 'M':       /* Write LLLL bytes address AA..AA, ret OK */
-                       write_mem_msg(0);       /* 0 = data in hex */
-                       break;
-
-               case 'X':       /* Write LLLL bytes esc bin address AA..AA */
-                       if (kgdb_bits == '8')
-                               write_mem_msg(1); /* 1 = data in binary */
-                       else
-                               send_empty_msg();
-                       break;
-
-               case 'C':       /* Continue, signum included, we ignore it */
-                       continue_with_sig_msg();
-                       return;
-
-               case 'c':       /* Continue at address AA..AA (optional) */
-                       continue_msg();
-                       return;
-
-               case 'S':       /* Step, signum included, we ignore it */
-                       step_with_sig_msg();
-                       return;
-
-               case 's':       /* Step one instruction from AA..AA */
-                       step_msg();
-                       return;
-
-               case 'k':       /* 'Kill the program' with a kernel ? */
-                       break;
-
-               case 'D':       /* Detach from program, send reply OK */
-                       kgdb_in_gdb_mode = 0;
-                       send_ok_msg();
-                       get_debug_char();
-                       return;
-
-               default:
-                       send_empty_msg();
-                       break;
-               }
-       }
-}
-
-/* There has been an exception, most likely a breakpoint. */
-static void handle_exception(struct pt_regs *regs)
-{
-       int excep_code, vbr_val;
-       int count;
-       int trapa_value = ctrl_inl(TRA);
-
-       /* Copy kernel regs (from stack) */
-       for (count = 0; count < 16; count++)
-               trap_registers.regs[count] = regs->regs[count];
-       trap_registers.pc = regs->pc;
-       trap_registers.pr = regs->pr;
-       trap_registers.sr = regs->sr;
-       trap_registers.gbr = regs->gbr;
-       trap_registers.mach = regs->mach;
-       trap_registers.macl = regs->macl;
-
-       asm("stc vbr, %0":"=r"(vbr_val));
-       trap_registers.vbr = vbr_val;
-
-       /* Get excode for command loop call, user access */
-       asm("stc r2_bank, %0":"=r"(excep_code));
-
-       /* Act on the exception */
-       kgdb_command_loop(excep_code, trapa_value);
-
-       /* Copy back the (maybe modified) registers */
-       for (count = 0; count < 16; count++)
-               regs->regs[count] = trap_registers.regs[count];
-       regs->pc = trap_registers.pc;
-       regs->pr = trap_registers.pr;
-       regs->sr = trap_registers.sr;
-       regs->gbr = trap_registers.gbr;
-       regs->mach = trap_registers.mach;
-       regs->macl = trap_registers.macl;
-
-       vbr_val = trap_registers.vbr;
-       asm("ldc %0, vbr": :"r"(vbr_val));
-}
-
-asmlinkage void kgdb_handle_exception(unsigned long r4, unsigned long r5,
-                                     unsigned long r6, unsigned long r7,
-                                     struct pt_regs __regs)
-{
-       struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
-       handle_exception(regs);
-}
-
-/* Initialise the KGDB data structures and serial configuration */
-int __init kgdb_init(void)
-{
-       in_nmi = 0;
-       kgdb_nofault = 0;
-       stepped_opcode = 0;
-       kgdb_in_gdb_mode = 0;
-
-       if (kgdb_serial_setup() != 0) {
-               printk(KERN_NOTICE "KGDB: serial setup error\n");
-               return -1;
-       }
-
-       /* Init ptr to exception handler */
-       kgdb_debug_hook = handle_exception;
-       kgdb_bus_err_hook = kgdb_handle_bus_error;
-
-       /* Enter kgdb now if requested, or just report init done */
-       printk(KERN_NOTICE "KGDB: stub is initialized.\n");
-
-       return 0;
-}
-
-/* Make function available for "user messages"; console will use it too. */
-
-char gdbmsgbuf[BUFMAX];
-#define MAXOUT ((BUFMAX-2)/2)
-
-static void kgdb_msg_write(const char *s, unsigned count)
-{
-       int i;
-       int wcount;
-       char *bufptr;
-
-       /* 'O'utput */
-       gdbmsgbuf[0] = 'O';
-
-       /* Fill and send buffers... */
-       while (count > 0) {
-               bufptr = gdbmsgbuf + 1;
-
-               /* Calculate how many this time */
-               wcount = (count > MAXOUT) ? MAXOUT : count;
-
-               /* Pack in hex chars */
-               for (i = 0; i < wcount; i++)
-                       bufptr = pack_hex_byte(bufptr, s[i]);
-               *bufptr = '\0';
-
-               /* Move up */
-               s += wcount;
-               count -= wcount;
-
-               /* Write packet */
-               put_packet(gdbmsgbuf);
-       }
-}
-
-static void kgdb_to_gdb(const char *s)
-{
-       kgdb_msg_write(s, strlen(s));
-}
-
-#ifdef CONFIG_SH_KGDB_CONSOLE
-void kgdb_console_write(struct console *co, const char *s, unsigned count)
-{
-       /* Bail if we're not talking to GDB */
-       if (!kgdb_in_gdb_mode)
-               return;
-
-       kgdb_msg_write(s, count);
-}
-#endif
-
-#ifdef CONFIG_KGDB_SYSRQ
-static void sysrq_handle_gdb(int key, struct tty_struct *tty)
-{
-       printk("Entering GDB stub\n");
-       breakpoint();
-}
-
-static struct sysrq_key_op sysrq_gdb_op = {
-        .handler        = sysrq_handle_gdb,
-        .help_msg       = "Gdb",
-        .action_msg     = "GDB",
-};
-
-static int gdb_register_sysrq(void)
-{
-       printk("Registering GDB sysrq handler\n");
-       register_sysrq_key('g', &sysrq_gdb_op);
-       return 0;
-}
-module_init(gdb_register_sysrq);
-#endif
diff --git a/arch/sh/kernel/pm.c b/arch/sh/kernel/pm.c
deleted file mode 100644 (file)
index 10ab62c..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Generic Power Management Routine
- *
- * Copyright (c) 2006 Andriy Skulysh <askulsyh@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License.
- */
-#include <linux/suspend.h>
-#include <linux/delay.h>
-#include <linux/gfp.h>
-#include <asm/freq.h>
-#include <asm/io.h>
-#include <asm/watchdog.h>
-#include <asm/pm.h>
-
-#define INTR_OFFSET    0x600
-
-#define STBCR          0xffffff82
-#define STBCR2         0xffffff88
-
-#define STBCR_STBY     0x80
-#define STBCR_MSTP2    0x04
-
-#define MCR            0xffffff68
-#define RTCNT          0xffffff70
-
-#define MCR_RMODE      2
-#define MCR_RFSH       4
-
-void pm_enter(void)
-{
-       u8 stbcr, csr;
-       u16 frqcr, mcr;
-       u32 vbr_new, vbr_old;
-
-       set_bl_bit();
-
-       /* set wdt */
-       csr = sh_wdt_read_csr();
-       csr &= ~WTCSR_TME;
-       csr |= WTCSR_CKS_4096;
-       sh_wdt_write_csr(csr);
-       csr = sh_wdt_read_csr();
-       sh_wdt_write_cnt(0);
-
-       /* disable PLL1 */
-       frqcr = ctrl_inw(FRQCR);
-       frqcr &= ~(FRQCR_PLLEN | FRQCR_PSTBY);
-       ctrl_outw(frqcr, FRQCR);
-
-       /* enable standby */
-       stbcr = ctrl_inb(STBCR);
-       ctrl_outb(stbcr | STBCR_STBY | STBCR_MSTP2, STBCR);
-
-       /* set self-refresh */
-       mcr = ctrl_inw(MCR);
-       ctrl_outw(mcr & ~MCR_RFSH, MCR);
-
-       /* set interrupt handler */
-       asm volatile("stc vbr, %0" : "=r" (vbr_old));
-       vbr_new = get_zeroed_page(GFP_ATOMIC);
-       udelay(50);
-       memcpy((void*)(vbr_new + INTR_OFFSET),
-              &wakeup_start, &wakeup_end - &wakeup_start);
-       asm volatile("ldc %0, vbr" : : "r" (vbr_new));
-
-       ctrl_outw(0, RTCNT);
-       ctrl_outw(mcr | MCR_RFSH | MCR_RMODE, MCR);
-
-       cpu_sleep();
-
-       asm volatile("ldc %0, vbr" : : "r" (vbr_old));
-
-       free_page(vbr_new);
-
-       /* enable PLL1 */
-       frqcr = ctrl_inw(FRQCR);
-       frqcr |= FRQCR_PSTBY;
-       ctrl_outw(frqcr, FRQCR);
-       udelay(50);
-       frqcr |= FRQCR_PLLEN;
-       ctrl_outw(frqcr, FRQCR);
-
-       ctrl_outb(stbcr, STBCR);
-
-       clear_bl_bit();
-}
index b965f02..ddafbbb 100644 (file)
 #include <asm/fpu.h>
 #include <asm/syscalls.h>
 
-static int hlt_counter;
 int ubc_usercnt = 0;
 
-void (*pm_idle)(void);
-void (*pm_power_off)(void);
-EXPORT_SYMBOL(pm_power_off);
-
-static int __init nohlt_setup(char *__unused)
-{
-       hlt_counter = 1;
-       return 1;
-}
-__setup("nohlt", nohlt_setup);
-
-static int __init hlt_setup(char *__unused)
-{
-       hlt_counter = 0;
-       return 1;
-}
-__setup("hlt", hlt_setup);
-
-static void default_idle(void)
-{
-       if (!hlt_counter) {
-               clear_thread_flag(TIF_POLLING_NRFLAG);
-               smp_mb__after_clear_bit();
-               set_bl_bit();
-               while (!need_resched())
-                       cpu_sleep();
-               clear_bl_bit();
-               set_thread_flag(TIF_POLLING_NRFLAG);
-       } else
-               while (!need_resched())
-                       cpu_relax();
-}
-
-void cpu_idle(void)
-{
-       set_thread_flag(TIF_POLLING_NRFLAG);
-
-       /* endless idle loop with no priority at all */
-       while (1) {
-               void (*idle)(void) = pm_idle;
-
-               if (!idle)
-                       idle = default_idle;
-
-               tick_nohz_stop_sched_tick(1);
-               while (!need_resched())
-                       idle();
-               tick_nohz_restart_sched_tick();
-
-               preempt_enable_no_resched();
-               schedule();
-               preempt_disable();
-               check_pgt_cache();
-       }
-}
-
 void machine_restart(char * __unused)
 {
        /* SR.BL=1 and invoke address error to let CPU reset (manual reset) */
@@ -115,8 +58,8 @@ void machine_power_off(void)
 void show_regs(struct pt_regs * regs)
 {
        printk("\n");
-       printk("Pid : %d, Comm: %20s\n", task_pid_nr(current), current->comm);
-       printk("CPU : %d    %s  (%s %.*s)\n",
+       printk("Pid : %d, Comm: \t\t%s\n", task_pid_nr(current), current->comm);
+       printk("CPU : %d        \t\t%s  (%s %.*s)\n\n",
               smp_processor_id(), print_tainted(), init_utsname()->release,
               (int)strcspn(init_utsname()->version, " "),
               init_utsname()->version);
@@ -148,26 +91,16 @@ void show_regs(struct pt_regs * regs)
               regs->mach, regs->macl, regs->gbr, regs->pr);
 
        show_trace(NULL, (unsigned long *)regs->regs[15], regs);
+       show_code(regs);
 }
 
 /*
  * Create a kernel thread
  */
-
-/*
- * This is the mechanism for creating a new kernel thread.
- *
- */
-extern void kernel_thread_helper(void);
-__asm__(".align 5\n"
-       "kernel_thread_helper:\n\t"
-       "jsr    @r5\n\t"
-       " nop\n\t"
-       "mov.l  1f, r1\n\t"
-       "jsr    @r1\n\t"
-       " mov   r0, r4\n\t"
-       ".align 2\n\t"
-       "1:.long do_exit");
+ATTRIB_NORET void kernel_thread_helper(void *arg, int (*fn)(void *))
+{
+       do_exit(fn(arg));
+}
 
 /* Don't use this in BL=1(cli).  Or else, CPU resets! */
 int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
index b7aa092..a7e5f2e 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/reboot.h>
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/proc_fs.h>
 #include <linux/io.h>
 #include <asm/syscalls.h>
 #include <asm/uaccess.h>
 
 struct task_struct *last_task_used_math = NULL;
 
-static int hlt_counter = 1;
-
-#define HARD_IDLE_TIMEOUT (HZ / 3)
-
-static int __init nohlt_setup(char *__unused)
-{
-       hlt_counter = 1;
-       return 1;
-}
-
-static int __init hlt_setup(char *__unused)
-{
-       hlt_counter = 0;
-       return 1;
-}
-
-__setup("nohlt", nohlt_setup);
-__setup("hlt", hlt_setup);
-
-static inline void hlt(void)
-{
-       __asm__ __volatile__ ("sleep" : : : "memory");
-}
-
-/*
- * The idle loop on a uniprocessor SH..
- */
-void cpu_idle(void)
-{
-       /* endless idle loop with no priority at all */
-       while (1) {
-               if (hlt_counter) {
-                       while (!need_resched())
-                               cpu_relax();
-               } else {
-                       local_irq_disable();
-                       while (!need_resched()) {
-                               local_irq_enable();
-                               hlt();
-                               local_irq_disable();
-                       }
-                       local_irq_enable();
-               }
-               preempt_enable_no_resched();
-               schedule();
-               preempt_disable();
-       }
-
-}
-
 void machine_restart(char * __unused)
 {
        extern void phys_stext(void);
@@ -97,13 +46,6 @@ void machine_halt(void)
 
 void machine_power_off(void)
 {
-#if 0
-       /* Disable watchdog timer */
-       ctrl_outl(0xa5000000, WTCSR);
-       /* Configure deep standby on sleep */
-       ctrl_outl(0x03, STBCR);
-#endif
-
        __asm__ __volatile__ (
                "sleep\n\t"
                "synci\n\t"
@@ -113,9 +55,6 @@ void machine_power_off(void)
        panic("Unexpected wakeup!\n");
 }
 
-void (*pm_power_off)(void) = machine_power_off;
-EXPORT_SYMBOL(pm_power_off);
-
 void show_regs(struct pt_regs * regs)
 {
        unsigned long long ah, al, bh, bl, ch, cl;
@@ -365,18 +304,6 @@ void show_regs(struct pt_regs * regs)
        }
 }
 
-struct task_struct * alloc_task_struct(void)
-{
-       /* Get task descriptor pages */
-       return (struct task_struct *)
-               __get_free_pages(GFP_KERNEL, get_order(THREAD_SIZE));
-}
-
-void free_task_struct(struct task_struct *p)
-{
-       free_pages((unsigned long) p, get_order(THREAD_SIZE));
-}
-
 /*
  * Create a kernel thread
  */
@@ -662,41 +589,3 @@ unsigned long get_wchan(struct task_struct *p)
 #endif
        return pc;
 }
-
-/* Provide a /proc/asids file that lists out the
-   ASIDs currently associated with the processes.  (If the DM.PC register is
-   examined through the debug link, this shows ASID + PC.  To make use of this,
-   the PID->ASID relationship needs to be known.  This is primarily for
-   debugging.)
-   */
-
-#if defined(CONFIG_SH64_PROC_ASIDS)
-static int
-asids_proc_info(char *buf, char **start, off_t fpos, int length, int *eof, void *data)
-{
-       int len=0;
-       struct task_struct *p;
-       read_lock(&tasklist_lock);
-       for_each_process(p) {
-               int pid = p->pid;
-
-               if (!pid)
-                       continue;
-               if (p->mm)
-                       len += sprintf(buf+len, "%5d : %02lx\n", pid,
-                                      asid_cache(smp_processor_id()));
-               else
-                       len += sprintf(buf+len, "%5d : (none)\n", pid);
-       }
-       read_unlock(&tasklist_lock);
-       *eof = 1;
-       return len;
-}
-
-static int __init register_proc_asids(void)
-{
-       create_proc_read_entry("asids", 0, NULL, asids_proc_info, NULL);
-       return 0;
-}
-__initcall(register_proc_asids);
-#endif
index e15b099..6950974 100644 (file)
@@ -2,7 +2,7 @@
  * arch/sh/kernel/ptrace_64.c
  *
  * Copyright (C) 2000, 2001  Paolo Alberelli
- * Copyright (C) 2003 - 2007  Paul Mundt
+ * Copyright (C) 2003 - 2008  Paul Mundt
  *
  * Started from SH3/4 version:
  *   SuperH version:   Copyright (C) 1999, 2000  Kaz Kojima & Niibe Yutaka
@@ -29,6 +29,8 @@
 #include <linux/audit.h>
 #include <linux/seccomp.h>
 #include <linux/tracehook.h>
+#include <linux/elf.h>
+#include <linux/regset.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -137,6 +139,165 @@ void user_disable_single_step(struct task_struct *child)
        regs->sr &= ~SR_SSTEP;
 }
 
+static int genregs_get(struct task_struct *target,
+                      const struct user_regset *regset,
+                      unsigned int pos, unsigned int count,
+                      void *kbuf, void __user *ubuf)
+{
+       const struct pt_regs *regs = task_pt_regs(target);
+       int ret;
+
+       /* PC, SR, SYSCALL */
+       ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+                                 &regs->pc,
+                                 0, 3 * sizeof(unsigned long long));
+
+       /* R1 -> R63 */
+       if (!ret)
+               ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+                                         regs->regs,
+                                         offsetof(struct pt_regs, regs[0]),
+                                         63 * sizeof(unsigned long long));
+       /* TR0 -> TR7 */
+       if (!ret)
+               ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+                                         regs->tregs,
+                                         offsetof(struct pt_regs, tregs[0]),
+                                         8 * sizeof(unsigned long long));
+
+       if (!ret)
+               ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
+                                              sizeof(struct pt_regs), -1);
+
+       return ret;
+}
+
+static int genregs_set(struct task_struct *target,
+                      const struct user_regset *regset,
+                      unsigned int pos, unsigned int count,
+                      const void *kbuf, const void __user *ubuf)
+{
+       struct pt_regs *regs = task_pt_regs(target);
+       int ret;
+
+       /* PC, SR, SYSCALL */
+       ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+                                &regs->pc,
+                                0, 3 * sizeof(unsigned long long));
+
+       /* R1 -> R63 */
+       if (!ret && count > 0)
+               ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+                                        regs->regs,
+                                        offsetof(struct pt_regs, regs[0]),
+                                        63 * sizeof(unsigned long long));
+
+       /* TR0 -> TR7 */
+       if (!ret && count > 0)
+               ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+                                        regs->tregs,
+                                        offsetof(struct pt_regs, tregs[0]),
+                                        8 * sizeof(unsigned long long));
+
+       if (!ret)
+               ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
+                                               sizeof(struct pt_regs), -1);
+
+       return ret;
+}
+
+#ifdef CONFIG_SH_FPU
+int fpregs_get(struct task_struct *target,
+              const struct user_regset *regset,
+              unsigned int pos, unsigned int count,
+              void *kbuf, void __user *ubuf)
+{
+       int ret;
+
+       ret = init_fpu(target);
+       if (ret)
+               return ret;
+
+       return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+                                  &target->thread.fpu.hard, 0, -1);
+}
+
+static int fpregs_set(struct task_struct *target,
+                      const struct user_regset *regset,
+                      unsigned int pos, unsigned int count,
+                      const void *kbuf, const void __user *ubuf)
+{
+       int ret;
+
+       ret = init_fpu(target);
+       if (ret)
+               return ret;
+
+       set_stopped_child_used_math(target);
+
+       return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+                                 &target->thread.fpu.hard, 0, -1);
+}
+
+static int fpregs_active(struct task_struct *target,
+                        const struct user_regset *regset)
+{
+       return tsk_used_math(target) ? regset->n : 0;
+}
+#endif
+
+/*
+ * These are our native regset flavours.
+ */
+enum sh_regset {
+       REGSET_GENERAL,
+#ifdef CONFIG_SH_FPU
+       REGSET_FPU,
+#endif
+};
+
+static const struct user_regset sh_regsets[] = {
+       /*
+        * Format is:
+        *      PC, SR, SYSCALL,
+        *      R1 --> R63,
+        *      TR0 --> TR7,
+        */
+       [REGSET_GENERAL] = {
+               .core_note_type = NT_PRSTATUS,
+               .n              = ELF_NGREG,
+               .size           = sizeof(long long),
+               .align          = sizeof(long long),
+               .get            = genregs_get,
+               .set            = genregs_set,
+       },
+
+#ifdef CONFIG_SH_FPU
+       [REGSET_FPU] = {
+               .core_note_type = NT_PRFPREG,
+               .n              = sizeof(struct user_fpu_struct) /
+                                 sizeof(long long),
+               .size           = sizeof(long long),
+               .align          = sizeof(long long),
+               .get            = fpregs_get,
+               .set            = fpregs_set,
+               .active         = fpregs_active,
+       },
+#endif
+};
+
+static const struct user_regset_view user_sh64_native_view = {
+       .name           = "sh64",
+       .e_machine      = EM_SH,
+       .regsets        = sh_regsets,
+       .n              = ARRAY_SIZE(sh_regsets),
+};
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+       return &user_sh64_native_view;
+}
+
 long arch_ptrace(struct task_struct *child, long request, long addr, long data)
 {
        int ret;
@@ -195,10 +356,33 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
                }
                break;
 
+       case PTRACE_GETREGS:
+               return copy_regset_to_user(child, &user_sh64_native_view,
+                                          REGSET_GENERAL,
+                                          0, sizeof(struct pt_regs),
+                                          (void __user *)data);
+       case PTRACE_SETREGS:
+               return copy_regset_from_user(child, &user_sh64_native_view,
+                                            REGSET_GENERAL,
+                                            0, sizeof(struct pt_regs),
+                                            (const void __user *)data);
+#ifdef CONFIG_SH_FPU
+       case PTRACE_GETFPREGS:
+               return copy_regset_to_user(child, &user_sh64_native_view,
+                                          REGSET_FPU,
+                                          0, sizeof(struct user_fpu_struct),
+                                          (void __user *)data);
+       case PTRACE_SETFPREGS:
+               return copy_regset_from_user(child, &user_sh64_native_view,
+                                            REGSET_FPU,
+                                            0, sizeof(struct user_fpu_struct),
+                                            (const void __user *)data);
+#endif
        default:
                ret = ptrace_request(child, request, addr, data);
                break;
        }
+
        return ret;
 }
 
index e7152cc..5342475 100644 (file)
@@ -417,6 +417,7 @@ void __init setup_arch(char **cmdline_p)
 }
 
 static const char *cpu_name[] = {
+       [CPU_SH7201]    = "SH7201",
        [CPU_SH7203]    = "SH7203",     [CPU_SH7263]    = "SH7263",
        [CPU_SH7206]    = "SH7206",     [CPU_SH7619]    = "SH7619",
        [CPU_SH7705]    = "SH7705",     [CPU_SH7706]    = "SH7706",
index d1bcac4..c852f78 100644 (file)
@@ -8,69 +8,50 @@
 #include <linux/module.h>
 #include <asm/sh_bios.h>
 
-#define BIOS_CALL_CONSOLE_WRITE        0
-#define BIOS_CALL_READ_BLOCK           1
+#define BIOS_CALL_CONSOLE_WRITE                0
 #define BIOS_CALL_ETH_NODE_ADDR                10
 #define BIOS_CALL_SHUTDOWN             11
-#define BIOS_CALL_CHAR_OUT                     0x1f    /* TODO: hack */
-#define BIOS_CALL_GDB_GET_MODE_PTR             0xfe
-#define BIOS_CALL_GDB_DETACH           0xff
+#define BIOS_CALL_CHAR_OUT             0x1f    /* TODO: hack */
+#define BIOS_CALL_GDB_DETACH           0xff
 
-static __inline__ long sh_bios_call(long func, long arg0, long arg1, long arg2, long arg3)
+static inline long sh_bios_call(long func, long arg0, long arg1, long arg2,
+                                   long arg3)
 {
-    register long r0 __asm__("r0") = func;
-    register long r4 __asm__("r4") = arg0;
-    register long r5 __asm__("r5") = arg1;
-    register long r6 __asm__("r6") = arg2;
-    register long r7 __asm__("r7") = arg3;
-    __asm__ __volatile__("trapa        #0x3f"
-        : "=z" (r0)
-        : "0" (r0), "r" (r4), "r" (r5), "r" (r6), "r" (r7)
-        "memory");
-    return r0;
+       register long r0 __asm__("r0") = func;
+       register long r4 __asm__("r4") = arg0;
+       register long r5 __asm__("r5") = arg1;
+       register long r6 __asm__("r6") = arg2;
+       register long r7 __asm__("r7") = arg3;
+
+       __asm__ __volatile__("trapa     #0x3f":"=z"(r0)
+                            :"0"(r0), "r"(r4), "r"(r5), "r"(r6), "r"(r7)
+                            :"memory");
+       return r0;
 }
 
-
 void sh_bios_console_write(const char *buf, unsigned int len)
 {
-    sh_bios_call(BIOS_CALL_CONSOLE_WRITE, (long)buf, (long)len, 0, 0);
+       sh_bios_call(BIOS_CALL_CONSOLE_WRITE, (long)buf, (long)len, 0, 0);
 }
 
-
 void sh_bios_char_out(char ch)
 {
-    sh_bios_call(BIOS_CALL_CHAR_OUT, ch, 0, 0, 0);
-}
-
-
-int sh_bios_in_gdb_mode(void)
-{
-    static char queried = 0;
-    static char *gdb_mode_p = 0;
-
-    if (!queried)
-    {
-       /* Query the gdb stub for address of its gdb mode variable */
-       long r = sh_bios_call(BIOS_CALL_GDB_GET_MODE_PTR, 0, 0, 0, 0);
-       if (r != ~0)    /* BIOS returns -1 for unknown function */
-           gdb_mode_p = (char *)r;
-       queried = 1;
-    }
-    return (gdb_mode_p != 0 ? *gdb_mode_p : 0);
+       sh_bios_call(BIOS_CALL_CHAR_OUT, ch, 0, 0, 0);
 }
 
 void sh_bios_gdb_detach(void)
 {
-    sh_bios_call(BIOS_CALL_GDB_DETACH, 0, 0, 0, 0);
+       sh_bios_call(BIOS_CALL_GDB_DETACH, 0, 0, 0, 0);
 }
-EXPORT_SYMBOL(sh_bios_gdb_detach);
+EXPORT_SYMBOL_GPL(sh_bios_gdb_detach);
 
-void sh_bios_get_node_addr (unsigned char *node_addr)
+void sh_bios_get_node_addr(unsigned char *node_addr)
 {
-    sh_bios_call(BIOS_CALL_ETH_NODE_ADDR, 0, (long)node_addr, 0, 0);
+       sh_bios_call(BIOS_CALL_ETH_NODE_ADDR, 0, (long)node_addr, 0, 0);
 }
+EXPORT_SYMBOL_GPL(sh_bios_get_node_addr);
 
 void sh_bios_shutdown(unsigned int how)
 {
-    sh_bios_call(BIOS_CALL_SHUTDOWN, how, 0, 0, 0);
+       sh_bios_call(BIOS_CALL_SHUTDOWN, how, 0, 0, 0);
 }
index 92ae5e6..528de29 100644 (file)
@@ -52,16 +52,12 @@ EXPORT_SYMBOL(__const_udelay);
 
 #define DECLARE_EXPORT(name)           \
        extern void name(void);EXPORT_SYMBOL(name)
-#define MAYBE_DECLARE_EXPORT(name)     \
-       extern void name(void) __weak;EXPORT_SYMBOL(name)
 
-/* These symbols are generated by the compiler itself */
 DECLARE_EXPORT(__udivsi3);
 DECLARE_EXPORT(__sdivsi3);
+DECLARE_EXPORT(__lshrsi3);
 DECLARE_EXPORT(__ashrsi3);
 DECLARE_EXPORT(__ashlsi3);
-DECLARE_EXPORT(__ashrdi3);
-DECLARE_EXPORT(__ashldi3);
 DECLARE_EXPORT(__ashiftrt_r4_6);
 DECLARE_EXPORT(__ashiftrt_r4_7);
 DECLARE_EXPORT(__ashiftrt_r4_8);
@@ -79,8 +75,7 @@ DECLARE_EXPORT(__ashiftrt_r4_23);
 DECLARE_EXPORT(__ashiftrt_r4_24);
 DECLARE_EXPORT(__ashiftrt_r4_27);
 DECLARE_EXPORT(__ashiftrt_r4_30);
-DECLARE_EXPORT(__lshrsi3);
-DECLARE_EXPORT(__lshrdi3);
+DECLARE_EXPORT(__movstr);
 DECLARE_EXPORT(__movstrSI8);
 DECLARE_EXPORT(__movstrSI12);
 DECLARE_EXPORT(__movstrSI16);
@@ -95,31 +90,17 @@ DECLARE_EXPORT(__movstrSI48);
 DECLARE_EXPORT(__movstrSI52);
 DECLARE_EXPORT(__movstrSI56);
 DECLARE_EXPORT(__movstrSI60);
-#if __GNUC__ == 4
-DECLARE_EXPORT(__movmem);
-#else
-DECLARE_EXPORT(__movstr);
-#endif
-
-#if __GNUC__ == 4
+DECLARE_EXPORT(__movstr_i4_even);
+DECLARE_EXPORT(__movstr_i4_odd);
+DECLARE_EXPORT(__movstrSI12_i4);
 DECLARE_EXPORT(__movmem_i4_even);
 DECLARE_EXPORT(__movmem_i4_odd);
 DECLARE_EXPORT(__movmemSI12_i4);
-
-#if (__GNUC_MINOR__ >= 2 || defined(__GNUC_STM_RELEASE__))
-/*
- * GCC >= 4.2 emits these for division, as do GCC 4.1.x versions of the ST
- * compiler which include backported patches.
- */
 DECLARE_EXPORT(__udiv_qrnnd_16);
-MAYBE_DECLARE_EXPORT(__sdivsi3_i4i);
-MAYBE_DECLARE_EXPORT(__udivsi3_i4i);
-#endif
-#else /* GCC 3.x */
-DECLARE_EXPORT(__movstr_i4_even);
-DECLARE_EXPORT(__movstr_i4_odd);
-DECLARE_EXPORT(__movstrSI12_i4);
-#endif /* __GNUC__ == 4 */
+DECLARE_EXPORT(__sdivsi3_i4);
+DECLARE_EXPORT(__udivsi3_i4);
+DECLARE_EXPORT(__sdivsi3_i4i);
+DECLARE_EXPORT(__udivsi3_i4i);
 
 #if !defined(CONFIG_CACHE_OFF) && (defined(CONFIG_CPU_SH4) || \
        defined(CONFIG_SH7705_CACHE_32KB))
index 9324d32..0d74d6b 100644 (file)
@@ -65,15 +65,16 @@ EXPORT_SYMBOL(copy_page);
 EXPORT_SYMBOL(__copy_user);
 EXPORT_SYMBOL(empty_zero_page);
 EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memset);
 EXPORT_SYMBOL(__udelay);
 EXPORT_SYMBOL(__ndelay);
 EXPORT_SYMBOL(__const_udelay);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(strcpy);
 
 /* Ugh.  These come in from libgcc.a at link time. */
 #define DECLARE_EXPORT(name) extern void name(void);EXPORT_SYMBOL(name)
 
 DECLARE_EXPORT(__sdivsi3);
-DECLARE_EXPORT(__sdivsi3_2);
-DECLARE_EXPORT(__muldi3);
 DECLARE_EXPORT(__udivsi3);
 DECLARE_EXPORT(__div_table);
index 69d09c0..77c21bd 100644 (file)
@@ -533,7 +533,6 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info,
 {
        int ret;
 
-
        /* Set up the stack frame */
        if (ka->sa.sa_flags & SA_SIGINFO)
                ret = setup_rt_frame(sig, ka, info, oldset, regs);
index ce3e851..b22fdfa 100644 (file)
@@ -2,7 +2,7 @@
  * arch/sh/kernel/signal_64.c
  *
  * Copyright (C) 2000, 2001  Paolo Alberelli
- * Copyright (C) 2003  Paul Mundt
+ * Copyright (C) 2003 - 2008  Paul Mundt
  * Copyright (C) 2004  Richard Curnow
  *
  * This file is subject to the terms and conditions of the GNU General Public
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
-static void
+static int
 handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
                sigset_t *oldset, struct pt_regs * regs);
 
+static inline void
+handle_syscall_restart(struct pt_regs *regs, struct sigaction *sa)
+{
+       /* If we're not from a syscall, bail out */
+       if (regs->syscall_nr < 0)
+               return;
+
+       /* check for system call restart.. */
+       switch (regs->regs[REG_RET]) {
+               case -ERESTART_RESTARTBLOCK:
+               case -ERESTARTNOHAND:
+               no_system_call_restart:
+                       regs->regs[REG_RET] = -EINTR;
+                       regs->sr |= 1;
+                       break;
+
+               case -ERESTARTSYS:
+                       if (!(sa->sa_flags & SA_RESTART))
+                               goto no_system_call_restart;
+               /* fallthrough */
+               case -ERESTARTNOINTR:
+                       /* Decode syscall # */
+                       regs->regs[REG_RET] = regs->syscall_nr;
+                       regs->pc -= 4;
+                       break;
+       }
+}
+
 /*
  * Note that 'init' is a special process: it doesn't get signals it doesn't
  * want to handle. Thus you cannot kill init even with a SIGKILL even by
@@ -80,21 +108,23 @@ static int do_signal(struct pt_regs *regs, sigset_t *oldset)
                oldset = &current->blocked;
 
        signr = get_signal_to_deliver(&info, &ka, regs, 0);
-
        if (signr > 0) {
-               /* Whee!  Actually deliver the signal.  */
-               handle_signal(signr, &info, &ka, oldset, regs);
+               if (regs->sr & 1)
+                       handle_syscall_restart(regs, &ka.sa);
 
-               /*
-                * If a signal was successfully delivered, the saved sigmask
-                * is in its frame, and we can clear the TIF_RESTORE_SIGMASK
-                * flag.
-                */
-               if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                       clear_thread_flag(TIF_RESTORE_SIGMASK);
-
-               tracehook_signal_handler(signr, &info, &ka, regs, 0);
-               return 1;
+               /* Whee!  Actually deliver the signal.  */
+               if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
+                       /*
+                        * If a signal was successfully delivered, the
+                        * saved sigmask is in its frame, and we can
+                        * clear the TIF_RESTORE_SIGMASK flag.
+                        */
+                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
+                               clear_thread_flag(TIF_RESTORE_SIGMASK);
+
+                       tracehook_signal_handler(signr, &info, &ka, regs, 0);
+                       return 1;
+               }
        }
 
 no_signal:
@@ -129,7 +159,6 @@ no_signal:
 /*
  * Atomically swap in the new signal mask, and wait for a signal.
  */
-
 asmlinkage int
 sys_sigsuspend(old_sigset_t mask,
               unsigned long r3, unsigned long r4, unsigned long r5,
@@ -235,20 +264,16 @@ sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
        return do_sigaltstack(uss, uoss, REF_REG_SP);
 }
 
-
 /*
  * Do a signal return; undo the signal stack.
  */
-
-struct sigframe
-{
+struct sigframe {
        struct sigcontext sc;
        unsigned long extramask[_NSIG_WORDS-1];
        long long retcode[2];
 };
 
-struct rt_sigframe
-{
+struct rt_sigframe {
        struct siginfo __user *pinfo;
        void *puc;
        struct siginfo info;
@@ -450,7 +475,6 @@ badframe:
 /*
  * Set up a signal frame.
  */
-
 static int
 setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
                 unsigned long mask)
@@ -504,8 +528,8 @@ get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size)
 void sa_default_restorer(void);                /* See comments below */
 void sa_default_rt_restorer(void);     /* See comments below */
 
-static void setup_frame(int sig, struct k_sigaction *ka,
-                       sigset_t *set, struct pt_regs *regs)
+static int setup_frame(int sig, struct k_sigaction *ka,
+                      sigset_t *set, struct pt_regs *regs)
 {
        struct sigframe __user *frame;
        int err = 0;
@@ -596,23 +620,21 @@ static void setup_frame(int sig, struct k_sigaction *ka,
 
        set_fs(USER_DS);
 
-#if DEBUG_SIG
        /* Broken %016Lx */
-       printk("SIG deliver (#%d,%s:%d): sp=%p pc=%08Lx%08Lx link=%08Lx%08Lx\n",
-               signal,
-               current->comm, current->pid, frame,
-               regs->pc >> 32, regs->pc & 0xffffffff,
-               DEREF_REG_PR >> 32, DEREF_REG_PR & 0xffffffff);
-#endif
+       pr_debug("SIG deliver (#%d,%s:%d): sp=%p pc=%08Lx%08Lx link=%08Lx%08Lx\n",
+                signal, current->comm, current->pid, frame,
+                regs->pc >> 32, regs->pc & 0xffffffff,
+                DEREF_REG_PR >> 32, DEREF_REG_PR & 0xffffffff);
 
-       return;
+       return 0;
 
 give_sigsegv:
        force_sigsegv(sig, current);
+       return -EFAULT;
 }
 
-static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
-                          sigset_t *set, struct pt_regs *regs)
+static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+                         sigset_t *set, struct pt_regs *regs)
 {
        struct rt_sigframe __user *frame;
        int err = 0;
@@ -702,62 +724,46 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
 
        set_fs(USER_DS);
 
-#if DEBUG_SIG
-       /* Broken %016Lx */
-       printk("SIG deliver (#%d,%s:%d): sp=%p pc=%08Lx%08Lx link=%08Lx%08Lx\n",
-               signal,
-               current->comm, current->pid, frame,
-               regs->pc >> 32, regs->pc & 0xffffffff,
-               DEREF_REG_PR >> 32, DEREF_REG_PR & 0xffffffff);
-#endif
+       pr_debug("SIG deliver (#%d,%s:%d): sp=%p pc=%08Lx%08Lx link=%08Lx%08Lx\n",
+                signal, current->comm, current->pid, frame,
+                regs->pc >> 32, regs->pc & 0xffffffff,
+                DEREF_REG_PR >> 32, DEREF_REG_PR & 0xffffffff);
 
-       return;
+       return 0;
 
 give_sigsegv:
        force_sigsegv(sig, current);
+       return -EFAULT;
 }
 
 /*
  * OK, we're invoking a handler
  */
-
-static void
+static int
 handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
                sigset_t *oldset, struct pt_regs * regs)
 {
-       /* Are we from a system call? */
-       if (regs->syscall_nr >= 0) {
-               /* If so, check system call restarting.. */
-               switch (regs->regs[REG_RET]) {
-                       case -ERESTART_RESTARTBLOCK:
-                       case -ERESTARTNOHAND:
-                       no_system_call_restart:
-                               regs->regs[REG_RET] = -EINTR;
-                               break;
-
-                       case -ERESTARTSYS:
-                               if (!(ka->sa.sa_flags & SA_RESTART))
-                                       goto no_system_call_restart;
-                       /* fallthrough */
-                       case -ERESTARTNOINTR:
-                               /* Decode syscall # */
-                               regs->regs[REG_RET] = regs->syscall_nr;
-                               regs->pc -= 4;
-               }
-       }
+       int ret;
 
        /* Set up the stack frame */
        if (ka->sa.sa_flags & SA_SIGINFO)
-               setup_rt_frame(sig, ka, info, oldset, regs);
+               ret = setup_rt_frame(sig, ka, info, oldset, regs);
        else
-               setup_frame(sig, ka, oldset, regs);
+               ret = setup_frame(sig, ka, oldset, regs);
+
+       if (ka->sa.sa_flags & SA_ONESHOT)
+               ka->sa.sa_handler = SIG_DFL;
+
+       if (ret == 0) {
+               spin_lock_irq(&current->sighand->siglock);
+               sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+               if (!(ka->sa.sa_flags & SA_NODEFER))
+                       sigaddset(&current->blocked,sig);
+               recalc_sigpending();
+               spin_unlock_irq(&current->sighand->siglock);
+       }
 
-       spin_lock_irq(&current->sighand->siglock);
-       sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
-       if (!(ka->sa.sa_flags & SA_NODEFER))
-               sigaddset(&current->blocked,sig);
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
+       return ret;
 }
 
 asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
index 38f098c..58dfc02 100644 (file)
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/ipc.h>
-#include <asm/cacheflush.h>
 #include <asm/syscalls.h>
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
 
-unsigned long shm_align_mask = PAGE_SIZE - 1;  /* Sane caches */
-EXPORT_SYMBOL(shm_align_mask);
-
-#ifdef CONFIG_MMU
-/*
- * To avoid cache aliases, we map the shared page with same color.
- */
-#define COLOUR_ALIGN(addr, pgoff)                              \
-       ((((addr) + shm_align_mask) & ~shm_align_mask) +        \
-        (((pgoff) << PAGE_SHIFT) & shm_align_mask))
-
-unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
-       unsigned long len, unsigned long pgoff, unsigned long flags)
-{
-       struct mm_struct *mm = current->mm;
-       struct vm_area_struct *vma;
-       unsigned long start_addr;
-       int do_colour_align;
-
-       if (flags & MAP_FIXED) {
-               /* We do not accept a shared mapping if it would violate
-                * cache aliasing constraints.
-                */
-               if ((flags & MAP_SHARED) && (addr & shm_align_mask))
-                       return -EINVAL;
-               return addr;
-       }
-
-       if (unlikely(len > TASK_SIZE))
-               return -ENOMEM;
-
-       do_colour_align = 0;
-       if (filp || (flags & MAP_SHARED))
-               do_colour_align = 1;
-
-       if (addr) {
-               if (do_colour_align)
-                       addr = COLOUR_ALIGN(addr, pgoff);
-               else
-                       addr = PAGE_ALIGN(addr);
-
-               vma = find_vma(mm, addr);
-               if (TASK_SIZE - len >= addr &&
-                   (!vma || addr + len <= vma->vm_start))
-                       return addr;
-       }
-
-       if (len > mm->cached_hole_size) {
-               start_addr = addr = mm->free_area_cache;
-       } else {
-               mm->cached_hole_size = 0;
-               start_addr = addr = TASK_UNMAPPED_BASE;
-       }
-
-full_search:
-       if (do_colour_align)
-               addr = COLOUR_ALIGN(addr, pgoff);
-       else
-               addr = PAGE_ALIGN(mm->free_area_cache);
-
-       for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
-               /* At this point:  (!vma || addr < vma->vm_end). */
-               if (unlikely(TASK_SIZE - len < addr)) {
-                       /*
-                        * Start a new search - just in case we missed
-                        * some holes.
-                        */
-                       if (start_addr != TASK_UNMAPPED_BASE) {
-                               start_addr = addr = TASK_UNMAPPED_BASE;
-                               mm->cached_hole_size = 0;
-                               goto full_search;
-                       }
-                       return -ENOMEM;
-               }
-               if (likely(!vma || addr + len <= vma->vm_start)) {
-                       /*
-                        * Remember the place where we stopped the search:
-                        */
-                       mm->free_area_cache = addr + len;
-                       return addr;
-               }
-               if (addr + mm->cached_hole_size < vma->vm_start)
-                       mm->cached_hole_size = vma->vm_start - addr;
-
-               addr = vma->vm_end;
-               if (do_colour_align)
-                       addr = COLOUR_ALIGN(addr, pgoff);
-       }
-}
-#endif /* CONFIG_MMU */
-
 static inline long
 do_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
         unsigned long flags, int fd, unsigned long pgoff)
index 23ca711..8457f83 100644 (file)
@@ -125,11 +125,6 @@ void handle_timer_tick(void)
        if (current->pid)
                profile_tick(CPU_PROFILING);
 
-#ifdef CONFIG_HEARTBEAT
-       if (sh_mv.mv_heartbeat != NULL)
-               sh_mv.mv_heartbeat();
-#endif
-
        /*
         * Here we are in the timer irq handler. We just have irqs locally
         * disabled but we don't know if the timer_bh is running on the other
@@ -277,11 +272,4 @@ void __init time_init(void)
                       ((sh_hpt_frequency + 500) / 1000) / 1000,
                       ((sh_hpt_frequency + 500) / 1000) % 1000);
 
-#if defined(CONFIG_SH_KGDB)
-       /*
-        * Set up kgdb as requested. We do it here because the serial
-        * init uses the timer vars we just set up for figuring baud.
-        */
-       kgdb_init();
-#endif
 }
index bbb2af1..59d2a03 100644 (file)
@@ -240,11 +240,6 @@ static inline void do_timer_interrupt(void)
 
        do_timer(1);
 
-#ifdef CONFIG_HEARTBEAT
-       if (sh_mv.mv_heartbeat != NULL)
-               sh_mv.mv_heartbeat();
-#endif
-
        /*
         * If we have an externally synchronized Linux clock, then update
         * RTC clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
index fe453c0..c3d237e 100644 (file)
 #define MTU2_TIER_1    0xfffe4384
 #define MTU2_TSR_1     0xfffe4385
 #define MTU2_TCNT_1    0xfffe4386      /* 16-bit counter */
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7201)
+#define MTU2_TGRA_1    0xfffe4388
+#else
 #define MTU2_TGRA_1    0xfffe438a
+#endif
 
 #define STBCR3         0xfffe0408
 
index 1e5c74e..88807a2 100644 (file)
 #include <asm/fpu.h>
 #include <asm/kprobes.h>
 
-#ifdef CONFIG_SH_KGDB
-#include <asm/kgdb.h>
-#define CHK_REMOTE_DEBUG(regs)                 \
-{                                              \
-       if (kgdb_debug_hook && !user_mode(regs))\
-               (*kgdb_debug_hook)(regs);       \
-}
-#else
-#define CHK_REMOTE_DEBUG(regs)
-#endif
-
 #ifdef CONFIG_CPU_SH2
 # define TRAP_RESERVED_INST    4
 # define TRAP_ILLEGAL_SLOT_INST        6
@@ -94,7 +83,6 @@ void die(const char * str, struct pt_regs * regs, long err)
 
        printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter);
 
-       CHK_REMOTE_DEBUG(regs);
        print_modules();
        show_regs(regs);
 
@@ -683,13 +671,12 @@ asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5,
        error_code = lookup_exception_vector();
 
        local_irq_enable();
-       CHK_REMOTE_DEBUG(regs);
        force_sig(SIGILL, tsk);
        die_if_no_fixup("reserved instruction", regs, error_code);
 }
 
 #ifdef CONFIG_SH_FPU_EMU
-static int emulate_branch(unsigned short inst, struct pt_regsregs)
+static int emulate_branch(unsigned short inst, struct pt_regs *regs)
 {
        /*
         * bfs: 8fxx: PC+=d*2+4;
@@ -702,27 +689,32 @@ static int emulate_branch(unsigned short inst, struct pt_regs* regs)
         * jsr: 4x0b: PC=Rn      after PR=PC+4;
         * rts: 000b: PC=PR;
         */
-       if ((inst & 0xfd00) == 0x8d00) {
+       if (((inst & 0xf000) == 0xb000)  ||     /* bsr */
+           ((inst & 0xf0ff) == 0x0003)  ||     /* bsrf */
+           ((inst & 0xf0ff) == 0x400b))        /* jsr */
+               regs->pr = regs->pc + 4;
+
+       if ((inst & 0xfd00) == 0x8d00) {        /* bfs, bts */
                regs->pc += SH_PC_8BIT_OFFSET(inst);
                return 0;
        }
 
-       if ((inst & 0xe000) == 0xa000) {
+       if ((inst & 0xe000) == 0xa000) {        /* bra, bsr */
                regs->pc += SH_PC_12BIT_OFFSET(inst);
                return 0;
        }
 
-       if ((inst & 0xf0df) == 0x0003) {
+       if ((inst & 0xf0df) == 0x0003) {        /* braf, bsrf */
                regs->pc += regs->regs[(inst & 0x0f00) >> 8] + 4;
                return 0;
        }
 
-       if ((inst & 0xf0df) == 0x400b) {
+       if ((inst & 0xf0df) == 0x400b) {        /* jmp, jsr */
                regs->pc = regs->regs[(inst & 0x0f00) >> 8];
                return 0;
        }
 
-       if ((inst & 0xffff) == 0x000b) {
+       if ((inst & 0xffff) == 0x000b) {        /* rts */
                regs->pc = regs->pr;
                return 0;
        }
@@ -756,7 +748,6 @@ asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5,
        inst = lookup_exception_vector();
 
        local_irq_enable();
-       CHK_REMOTE_DEBUG(regs);
        force_sig(SIGILL, tsk);
        die_if_no_fixup("illegal slot instruction", regs, inst);
 }
@@ -868,10 +859,7 @@ void show_trace(struct task_struct *tsk, unsigned long *sp,
        if (regs && user_mode(regs))
                return;
 
-       printk("\nCall trace: ");
-#ifdef CONFIG_KALLSYMS
-       printk("\n");
-#endif
+       printk("\nCall trace:\n");
 
        while (!kstack_end(sp)) {
                addr = *sp++;
index 8596cc7..aaea580 100644 (file)
@@ -5,12 +5,26 @@
 lib-y  = delay.o memset.o memmove.o memchr.o \
         checksum.o strlen.o div64.o div64-generic.o
 
+# Extracted from libgcc
+lib-y += movmem.o ashldi3.o ashrdi3.o lshrdi3.o \
+        ashlsi3.o ashrsi3.o ashiftrt.o lshrsi3.o \
+        udiv_qrnnd.o
+
+udivsi3-y                      := udivsi3_i4i-Os.o
+
+ifneq ($(CONFIG_CC_OPTIMIZE_FOR_SIZE),y)
+udivsi3-$(CONFIG_CPU_SH3)      := udivsi3_i4i.o
+udivsi3-$(CONFIG_CPU_SH4)      := udivsi3_i4i.o
+endif
+udivsi3-y                      += udivsi3.o
+
 obj-y                          += io.o
 
 memcpy-y                       := memcpy.o
 memcpy-$(CONFIG_CPU_SH4)       := memcpy-sh4.o
 
 lib-$(CONFIG_MMU)              += copy_page.o clear_page.o
-lib-y                          += $(memcpy-y)
+lib-$(CONFIG_FUNCTION_TRACER)  += mcount.o
+lib-y                          += $(memcpy-y) $(udivsi3-y)
 
 EXTRA_CFLAGS += -Werror
diff --git a/arch/sh/lib/ashiftrt.S b/arch/sh/lib/ashiftrt.S
new file mode 100644 (file)
index 0000000..45ce865
--- /dev/null
@@ -0,0 +1,149 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+   2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file 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; see the file COPYING.  If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
+!! recoded in assembly by Toshiyasu Morita
+!! tm@netcom.com
+
+/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
+   ELF local label prefixes by J"orn Rennecke
+   amylaar@cygnus.com  */
+
+       .global __ashiftrt_r4_0
+       .global __ashiftrt_r4_1
+       .global __ashiftrt_r4_2
+       .global __ashiftrt_r4_3
+       .global __ashiftrt_r4_4
+       .global __ashiftrt_r4_5
+       .global __ashiftrt_r4_6
+       .global __ashiftrt_r4_7
+       .global __ashiftrt_r4_8
+       .global __ashiftrt_r4_9
+       .global __ashiftrt_r4_10
+       .global __ashiftrt_r4_11
+       .global __ashiftrt_r4_12
+       .global __ashiftrt_r4_13
+       .global __ashiftrt_r4_14
+       .global __ashiftrt_r4_15
+       .global __ashiftrt_r4_16
+       .global __ashiftrt_r4_17
+       .global __ashiftrt_r4_18
+       .global __ashiftrt_r4_19
+       .global __ashiftrt_r4_20
+       .global __ashiftrt_r4_21
+       .global __ashiftrt_r4_22
+       .global __ashiftrt_r4_23
+       .global __ashiftrt_r4_24
+       .global __ashiftrt_r4_25
+       .global __ashiftrt_r4_26
+       .global __ashiftrt_r4_27
+       .global __ashiftrt_r4_28
+       .global __ashiftrt_r4_29
+       .global __ashiftrt_r4_30
+       .global __ashiftrt_r4_31
+       .global __ashiftrt_r4_32
+
+       .align  1
+__ashiftrt_r4_32:
+__ashiftrt_r4_31:
+       rotcl   r4
+       rts
+       subc    r4,r4
+__ashiftrt_r4_30:
+       shar    r4
+__ashiftrt_r4_29:
+       shar    r4
+__ashiftrt_r4_28:
+       shar    r4
+__ashiftrt_r4_27:
+       shar    r4
+__ashiftrt_r4_26:
+       shar    r4
+__ashiftrt_r4_25:
+       shar    r4
+__ashiftrt_r4_24:
+       shlr16  r4
+       shlr8   r4
+       rts
+       exts.b  r4,r4
+__ashiftrt_r4_23:
+       shar    r4
+__ashiftrt_r4_22:
+       shar    r4
+__ashiftrt_r4_21:
+       shar    r4
+__ashiftrt_r4_20:
+       shar    r4
+__ashiftrt_r4_19:
+       shar    r4
+__ashiftrt_r4_18:
+       shar    r4
+__ashiftrt_r4_17:
+       shar    r4
+__ashiftrt_r4_16:
+       shlr16  r4
+       rts
+       exts.w  r4,r4
+__ashiftrt_r4_15:
+       shar    r4
+__ashiftrt_r4_14:
+       shar    r4
+__ashiftrt_r4_13:
+       shar    r4
+__ashiftrt_r4_12:
+       shar    r4
+__ashiftrt_r4_11:
+       shar    r4
+__ashiftrt_r4_10:
+       shar    r4
+__ashiftrt_r4_9:
+       shar    r4
+__ashiftrt_r4_8:
+       shar    r4
+__ashiftrt_r4_7:
+       shar    r4
+__ashiftrt_r4_6:
+       shar    r4
+__ashiftrt_r4_5:
+       shar    r4
+__ashiftrt_r4_4:
+       shar    r4
+__ashiftrt_r4_3:
+       shar    r4
+__ashiftrt_r4_2:
+       shar    r4
+__ashiftrt_r4_1:
+       rts
+       shar    r4
+__ashiftrt_r4_0:
+       rts
+       nop
diff --git a/arch/sh/lib/ashldi3.c b/arch/sh/lib/ashldi3.c
new file mode 100644 (file)
index 0000000..beb80f3
--- /dev/null
@@ -0,0 +1,29 @@
+#include <linux/module.h>
+
+#include "libgcc.h"
+
+long long __ashldi3(long long u, word_type b)
+{
+       DWunion uu, w;
+       word_type bm;
+
+       if (b == 0)
+               return u;
+
+       uu.ll = u;
+       bm = 32 - b;
+
+       if (bm <= 0) {
+               w.s.low = 0;
+               w.s.high = (unsigned int) uu.s.low << -bm;
+       } else {
+               const unsigned int carries = (unsigned int) uu.s.low >> bm;
+
+               w.s.low = (unsigned int) uu.s.low << b;
+               w.s.high = ((unsigned int) uu.s.high << b) | carries;
+       }
+
+       return w.ll;
+}
+
+EXPORT_SYMBOL(__ashldi3);
diff --git a/arch/sh/lib/ashlsi3.S b/arch/sh/lib/ashlsi3.S
new file mode 100644 (file)
index 0000000..bd47e9b
--- /dev/null
@@ -0,0 +1,193 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+   2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file 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; see the file COPYING.  If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
+!! recoded in assembly by Toshiyasu Morita
+!! tm@netcom.com
+
+/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
+   ELF local label prefixes by J"orn Rennecke
+   amylaar@cygnus.com  */
+
+!
+! __ashlsi3
+!
+! Entry:
+!
+! r4: Value to shift
+! r5: Shifts
+!
+! Exit:
+!
+! r0: Result
+!
+! Destroys:
+!
+! (none)
+!
+       .global __ashlsi3
+       
+       .align  2
+__ashlsi3:
+       mov     #31,r0
+       and     r0,r5
+       mova    ashlsi3_table,r0
+       mov.b   @(r0,r5),r5
+#ifdef __sh1__
+       add     r5,r0
+       jmp     @r0
+#else
+       braf    r5
+#endif
+       mov     r4,r0
+
+       .align  2
+ashlsi3_table:
+       .byte           ashlsi3_0-ashlsi3_table
+       .byte           ashlsi3_1-ashlsi3_table
+       .byte           ashlsi3_2-ashlsi3_table
+       .byte           ashlsi3_3-ashlsi3_table
+       .byte           ashlsi3_4-ashlsi3_table
+       .byte           ashlsi3_5-ashlsi3_table
+       .byte           ashlsi3_6-ashlsi3_table
+       .byte           ashlsi3_7-ashlsi3_table
+       .byte           ashlsi3_8-ashlsi3_table
+       .byte           ashlsi3_9-ashlsi3_table
+       .byte           ashlsi3_10-ashlsi3_table
+       .byte           ashlsi3_11-ashlsi3_table
+       .byte           ashlsi3_12-ashlsi3_table
+       .byte           ashlsi3_13-ashlsi3_table
+       .byte           ashlsi3_14-ashlsi3_table
+       .byte           ashlsi3_15-ashlsi3_table
+       .byte           ashlsi3_16-ashlsi3_table
+       .byte           ashlsi3_17-ashlsi3_table
+       .byte           ashlsi3_18-ashlsi3_table
+       .byte           ashlsi3_19-ashlsi3_table
+       .byte           ashlsi3_20-ashlsi3_table
+       .byte           ashlsi3_21-ashlsi3_table
+       .byte           ashlsi3_22-ashlsi3_table
+       .byte           ashlsi3_23-ashlsi3_table
+       .byte           ashlsi3_24-ashlsi3_table
+       .byte           ashlsi3_25-ashlsi3_table
+       .byte           ashlsi3_26-ashlsi3_table
+       .byte           ashlsi3_27-ashlsi3_table
+       .byte           ashlsi3_28-ashlsi3_table
+       .byte           ashlsi3_29-ashlsi3_table
+       .byte           ashlsi3_30-ashlsi3_table
+       .byte           ashlsi3_31-ashlsi3_table
+
+ashlsi3_6:
+       shll2   r0
+ashlsi3_4:
+       shll2   r0
+ashlsi3_2:
+       rts
+       shll2   r0
+
+ashlsi3_7:
+       shll2   r0
+ashlsi3_5:
+       shll2   r0
+ashlsi3_3:
+       shll2   r0
+ashlsi3_1:
+       rts
+       shll    r0
+
+ashlsi3_14:
+       shll2   r0
+ashlsi3_12:
+       shll2   r0
+ashlsi3_10:
+       shll2   r0
+ashlsi3_8:
+       rts
+       shll8   r0
+
+ashlsi3_15:
+       shll2   r0
+ashlsi3_13:
+       shll2   r0
+ashlsi3_11:
+       shll2   r0
+ashlsi3_9:
+       shll8   r0
+       rts
+       shll    r0
+
+ashlsi3_22:
+       shll2   r0
+ashlsi3_20:
+       shll2   r0
+ashlsi3_18:
+       shll2   r0
+ashlsi3_16:
+       rts
+       shll16  r0
+
+ashlsi3_23:
+       shll2   r0
+ashlsi3_21:
+       shll2   r0
+ashlsi3_19:
+       shll2   r0
+ashlsi3_17:
+       shll16  r0
+       rts
+       shll    r0
+
+ashlsi3_30:
+       shll2   r0
+ashlsi3_28:
+       shll2   r0
+ashlsi3_26:
+       shll2   r0
+ashlsi3_24:
+       shll16  r0
+       rts
+       shll8   r0
+
+ashlsi3_31:
+       shll2   r0
+ashlsi3_29:
+       shll2   r0
+ashlsi3_27:
+       shll2   r0
+ashlsi3_25:
+       shll16  r0
+       shll8   r0
+       rts
+       shll    r0
+
+ashlsi3_0:
+       rts
+       nop
diff --git a/arch/sh/lib/ashrdi3.c b/arch/sh/lib/ashrdi3.c
new file mode 100644 (file)
index 0000000..c884a91
--- /dev/null
@@ -0,0 +1,31 @@
+#include <linux/module.h>
+
+#include "libgcc.h"
+
+long long __ashrdi3(long long u, word_type b)
+{
+       DWunion uu, w;
+       word_type bm;
+
+       if (b == 0)
+               return u;
+
+       uu.ll = u;
+       bm = 32 - b;
+
+       if (bm <= 0) {
+               /* w.s.high = 1..1 or 0..0 */
+               w.s.high =
+                   uu.s.high >> 31;
+               w.s.low = uu.s.high >> -bm;
+       } else {
+               const unsigned int carries = (unsigned int) uu.s.high << bm;
+
+               w.s.high = uu.s.high >> b;
+               w.s.low = ((unsigned int) uu.s.low >> b) | carries;
+       }
+
+       return w.ll;
+}
+
+EXPORT_SYMBOL(__ashrdi3);
diff --git a/arch/sh/lib/ashrsi3.S b/arch/sh/lib/ashrsi3.S
new file mode 100644 (file)
index 0000000..6f3cf46
--- /dev/null
@@ -0,0 +1,185 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+   2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file 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; see the file COPYING.  If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
+!! recoded in assembly by Toshiyasu Morita
+!! tm@netcom.com
+
+/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
+   ELF local label prefixes by J"orn Rennecke
+   amylaar@cygnus.com  */
+
+!
+! __ashrsi3
+!
+! Entry:
+!
+! r4: Value to shift
+! r5: Shifts
+!
+! Exit:
+!
+! r0: Result
+!
+! Destroys:
+!
+! (none)
+!
+
+       .global __ashrsi3
+       
+       .align  2
+__ashrsi3:
+       mov     #31,r0
+       and     r0,r5
+       mova    ashrsi3_table,r0
+       mov.b   @(r0,r5),r5
+#ifdef __sh1__
+       add     r5,r0
+       jmp     @r0
+#else
+       braf    r5
+#endif
+       mov     r4,r0
+
+       .align  2
+ashrsi3_table:
+       .byte           ashrsi3_0-ashrsi3_table
+       .byte           ashrsi3_1-ashrsi3_table
+       .byte           ashrsi3_2-ashrsi3_table
+       .byte           ashrsi3_3-ashrsi3_table
+       .byte           ashrsi3_4-ashrsi3_table
+       .byte           ashrsi3_5-ashrsi3_table
+       .byte           ashrsi3_6-ashrsi3_table
+       .byte           ashrsi3_7-ashrsi3_table
+       .byte           ashrsi3_8-ashrsi3_table
+       .byte           ashrsi3_9-ashrsi3_table
+       .byte           ashrsi3_10-ashrsi3_table
+       .byte           ashrsi3_11-ashrsi3_table
+       .byte           ashrsi3_12-ashrsi3_table
+       .byte           ashrsi3_13-ashrsi3_table
+       .byte           ashrsi3_14-ashrsi3_table
+       .byte           ashrsi3_15-ashrsi3_table
+       .byte           ashrsi3_16-ashrsi3_table
+       .byte           ashrsi3_17-ashrsi3_table
+       .byte           ashrsi3_18-ashrsi3_table
+       .byte           ashrsi3_19-ashrsi3_table
+       .byte           ashrsi3_20-ashrsi3_table
+       .byte           ashrsi3_21-ashrsi3_table
+       .byte           ashrsi3_22-ashrsi3_table
+       .byte           ashrsi3_23-ashrsi3_table
+       .byte           ashrsi3_24-ashrsi3_table
+       .byte           ashrsi3_25-ashrsi3_table
+       .byte           ashrsi3_26-ashrsi3_table
+       .byte           ashrsi3_27-ashrsi3_table
+       .byte           ashrsi3_28-ashrsi3_table
+       .byte           ashrsi3_29-ashrsi3_table
+       .byte           ashrsi3_30-ashrsi3_table
+       .byte           ashrsi3_31-ashrsi3_table
+
+ashrsi3_31:
+       rotcl   r0
+       rts
+       subc    r0,r0
+
+ashrsi3_30:
+       shar    r0
+ashrsi3_29:
+       shar    r0
+ashrsi3_28:
+       shar    r0
+ashrsi3_27:
+       shar    r0
+ashrsi3_26:
+       shar    r0
+ashrsi3_25:
+       shar    r0
+ashrsi3_24:
+       shlr16  r0
+       shlr8   r0
+       rts
+       exts.b  r0,r0
+
+ashrsi3_23:
+       shar    r0
+ashrsi3_22:
+       shar    r0
+ashrsi3_21:
+       shar    r0
+ashrsi3_20:
+       shar    r0
+ashrsi3_19:
+       shar    r0
+ashrsi3_18:
+       shar    r0
+ashrsi3_17:
+       shar    r0
+ashrsi3_16:
+       shlr16  r0
+       rts
+       exts.w  r0,r0
+
+ashrsi3_15:
+       shar    r0
+ashrsi3_14:
+       shar    r0
+ashrsi3_13:
+       shar    r0
+ashrsi3_12:
+       shar    r0
+ashrsi3_11:
+       shar    r0
+ashrsi3_10:
+       shar    r0
+ashrsi3_9:
+       shar    r0
+ashrsi3_8:
+       shar    r0
+ashrsi3_7:
+       shar    r0
+ashrsi3_6:
+       shar    r0
+ashrsi3_5:
+       shar    r0
+ashrsi3_4:
+       shar    r0
+ashrsi3_3:
+       shar    r0
+ashrsi3_2:
+       shar    r0
+ashrsi3_1:
+       rts
+       shar    r0
+
+ashrsi3_0:
+       rts
+       nop
diff --git a/arch/sh/lib/libgcc.h b/arch/sh/lib/libgcc.h
new file mode 100644 (file)
index 0000000..3f19d1c
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef __ASM_LIBGCC_H
+#define __ASM_LIBGCC_H
+
+#include <asm/byteorder.h>
+
+typedef int word_type __attribute__ ((mode (__word__)));
+
+#ifdef __BIG_ENDIAN
+struct DWstruct {
+       int high, low;
+};
+#elif defined(__LITTLE_ENDIAN)
+struct DWstruct {
+       int low, high;
+};
+#else
+#error I feel sick.
+#endif
+
+typedef union
+{
+       struct DWstruct s;
+       long long ll;
+} DWunion;
+
+#endif /* __ASM_LIBGCC_H */
diff --git a/arch/sh/lib/lshrdi3.c b/arch/sh/lib/lshrdi3.c
new file mode 100644 (file)
index 0000000..dcf8d68
--- /dev/null
@@ -0,0 +1,29 @@
+#include <linux/module.h>
+
+#include "libgcc.h"
+
+long long __lshrdi3(long long u, word_type b)
+{
+       DWunion uu, w;
+       word_type bm;
+
+       if (b == 0)
+               return u;
+
+       uu.ll = u;
+       bm = 32 - b;
+
+       if (bm <= 0) {
+               w.s.high = 0;
+               w.s.low = (unsigned int) uu.s.high >> -bm;
+       } else {
+               const unsigned int carries = (unsigned int) uu.s.high << bm;
+
+               w.s.high = (unsigned int) uu.s.high >> b;
+               w.s.low = ((unsigned int) uu.s.low >> b) | carries;
+       }
+
+       return w.ll;
+}
+
+EXPORT_SYMBOL(__lshrdi3);
diff --git a/arch/sh/lib/lshrsi3.S b/arch/sh/lib/lshrsi3.S
new file mode 100644 (file)
index 0000000..1e7aaa5
--- /dev/null
@@ -0,0 +1,193 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+   2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file 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; see the file COPYING.  If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
+!! recoded in assembly by Toshiyasu Morita
+!! tm@netcom.com
+
+/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
+   ELF local label prefixes by J"orn Rennecke
+   amylaar@cygnus.com  */
+
+!
+! __lshrsi3
+!
+! Entry:
+!
+! r4: Value to shift
+! r5: Shifts
+!
+! Exit:
+!
+! r0: Result
+!
+! Destroys:
+!
+! (none)
+!
+       .global __lshrsi3
+       
+       .align  2
+__lshrsi3:
+       mov     #31,r0
+       and     r0,r5
+       mova    lshrsi3_table,r0
+       mov.b   @(r0,r5),r5
+#ifdef __sh1__
+       add     r5,r0
+       jmp     @r0
+#else
+       braf    r5
+#endif
+       mov     r4,r0
+
+       .align  2
+lshrsi3_table:
+       .byte           lshrsi3_0-lshrsi3_table
+       .byte           lshrsi3_1-lshrsi3_table
+       .byte           lshrsi3_2-lshrsi3_table
+       .byte           lshrsi3_3-lshrsi3_table
+       .byte           lshrsi3_4-lshrsi3_table
+       .byte           lshrsi3_5-lshrsi3_table
+       .byte           lshrsi3_6-lshrsi3_table
+       .byte           lshrsi3_7-lshrsi3_table
+       .byte           lshrsi3_8-lshrsi3_table
+       .byte           lshrsi3_9-lshrsi3_table
+       .byte           lshrsi3_10-lshrsi3_table
+       .byte           lshrsi3_11-lshrsi3_table
+       .byte           lshrsi3_12-lshrsi3_table
+       .byte           lshrsi3_13-lshrsi3_table
+       .byte           lshrsi3_14-lshrsi3_table
+       .byte           lshrsi3_15-lshrsi3_table
+       .byte           lshrsi3_16-lshrsi3_table
+       .byte           lshrsi3_17-lshrsi3_table
+       .byte           lshrsi3_18-lshrsi3_table
+       .byte           lshrsi3_19-lshrsi3_table
+       .byte           lshrsi3_20-lshrsi3_table
+       .byte           lshrsi3_21-lshrsi3_table
+       .byte           lshrsi3_22-lshrsi3_table
+       .byte           lshrsi3_23-lshrsi3_table
+       .byte           lshrsi3_24-lshrsi3_table
+       .byte           lshrsi3_25-lshrsi3_table
+       .byte           lshrsi3_26-lshrsi3_table
+       .byte           lshrsi3_27-lshrsi3_table
+       .byte           lshrsi3_28-lshrsi3_table
+       .byte           lshrsi3_29-lshrsi3_table
+       .byte           lshrsi3_30-lshrsi3_table
+       .byte           lshrsi3_31-lshrsi3_table
+
+lshrsi3_6:
+       shlr2   r0
+lshrsi3_4:
+       shlr2   r0
+lshrsi3_2:
+       rts
+       shlr2   r0
+
+lshrsi3_7:
+       shlr2   r0
+lshrsi3_5:
+       shlr2   r0
+lshrsi3_3:
+       shlr2   r0
+lshrsi3_1:
+       rts
+       shlr    r0
+
+lshrsi3_14:
+       shlr2   r0
+lshrsi3_12:
+       shlr2   r0
+lshrsi3_10:
+       shlr2   r0
+lshrsi3_8:
+       rts
+       shlr8   r0
+
+lshrsi3_15:
+       shlr2   r0
+lshrsi3_13:
+       shlr2   r0
+lshrsi3_11:
+       shlr2   r0
+lshrsi3_9:
+       shlr8   r0
+       rts
+       shlr    r0
+
+lshrsi3_22:
+       shlr2   r0
+lshrsi3_20:
+       shlr2   r0
+lshrsi3_18:
+       shlr2   r0
+lshrsi3_16:
+       rts
+       shlr16  r0
+
+lshrsi3_23:
+       shlr2   r0
+lshrsi3_21:
+       shlr2   r0
+lshrsi3_19:
+       shlr2   r0
+lshrsi3_17:
+       shlr16  r0
+       rts
+       shlr    r0
+
+lshrsi3_30:
+       shlr2   r0
+lshrsi3_28:
+       shlr2   r0
+lshrsi3_26:
+       shlr2   r0
+lshrsi3_24:
+       shlr16  r0
+       rts
+       shlr8   r0
+
+lshrsi3_31:
+       shlr2   r0
+lshrsi3_29:
+       shlr2   r0
+lshrsi3_27:
+       shlr2   r0
+lshrsi3_25:
+       shlr16  r0
+       shlr8   r0
+       rts
+       shlr    r0
+
+lshrsi3_0:
+       rts
+       nop
diff --git a/arch/sh/lib/mcount.S b/arch/sh/lib/mcount.S
new file mode 100644 (file)
index 0000000..110fbfe
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * arch/sh/lib/mcount.S
+ *
+ *  Copyright (C) 2008  Paul Mundt
+ *  Copyright (C) 2008  Matt Fleming
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <asm/ftrace.h>
+
+#define MCOUNT_ENTER()         \
+       mov.l   r4, @-r15;      \
+       mov.l   r5, @-r15;      \
+       mov.l   r6, @-r15;      \
+       mov.l   r7, @-r15;      \
+       sts.l   pr, @-r15;      \
+                               \
+       mov.l   @(20,r15),r4;   \
+       sts     pr, r5
+
+#define MCOUNT_LEAVE()         \
+       lds.l   @r15+, pr;      \
+       mov.l   @r15+, r7;      \
+       mov.l   @r15+, r6;      \
+       mov.l   @r15+, r5;      \
+       rts;                    \
+        mov.l  @r15+, r4
+
+       .align 2
+       .globl  _mcount
+       .type   _mcount,@function
+       .globl  mcount
+       .type   mcount,@function
+_mcount:
+mcount:
+       MCOUNT_ENTER()
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+       .globl  mcount_call
+mcount_call:
+       mov.l   .Lftrace_stub, r6
+#else
+       mov.l   .Lftrace_trace_function, r6
+       mov.l   ftrace_stub, r7
+       cmp/eq  r6, r7
+       bt      skip_trace
+       mov.l   @r6, r6
+#endif
+
+       jsr     @r6
+        nop
+
+skip_trace:
+       MCOUNT_LEAVE()
+
+       .align 2
+.Lftrace_trace_function:
+       .long   ftrace_trace_function
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+       .globl ftrace_caller
+ftrace_caller:
+       MCOUNT_ENTER()
+
+       .globl ftrace_call
+ftrace_call:
+       mov.l   .Lftrace_stub, r6
+       jsr     @r6
+        nop
+
+       MCOUNT_LEAVE()
+#endif /* CONFIG_DYNAMIC_FTRACE */
+
+/*
+ * NOTE: From here on the locations of the .Lftrace_stub label and
+ * ftrace_stub itself are fixed. Adding additional data here will skew
+ * the displacement for the memory table and break the block replacement.
+ * Place new labels either after the ftrace_stub body, or before
+ * ftrace_caller. You have been warned.
+ */
+       .align 2
+.Lftrace_stub:
+       .long   ftrace_stub
+
+       .globl  ftrace_stub
+ftrace_stub:
+       rts
+        nop
diff --git a/arch/sh/lib/movmem.S b/arch/sh/lib/movmem.S
new file mode 100644 (file)
index 0000000..62075f6
--- /dev/null
@@ -0,0 +1,238 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+   2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file 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; see the file COPYING.  If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
+!! recoded in assembly by Toshiyasu Morita
+!! tm@netcom.com
+
+/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
+   ELF local label prefixes by J"orn Rennecke
+   amylaar@cygnus.com  */
+
+       .text
+       .balign 4
+       .global __movmem
+       .global __movstr
+       .set __movstr, __movmem 
+       /* This would be a lot simpler if r6 contained the byte count
+          minus 64, and we wouldn't be called here for a byte count of 64.  */
+__movmem:
+       sts.l   pr,@-r15
+       shll2   r6
+       bsr     __movmemSI52+2
+       mov.l   @(48,r5),r0
+       .balign 4
+movmem_loop: /* Reached with rts */
+       mov.l   @(60,r5),r0
+       add     #-64,r6
+       mov.l   r0,@(60,r4)
+       tst     r6,r6
+       mov.l   @(56,r5),r0
+       bt      movmem_done
+       mov.l   r0,@(56,r4)
+       cmp/pl  r6
+       mov.l   @(52,r5),r0
+       add     #64,r5
+       mov.l   r0,@(52,r4)
+       add     #64,r4
+       bt      __movmemSI52
+! done all the large groups, do the remainder
+! jump to movmem+
+       mova    __movmemSI4+4,r0
+       add     r6,r0
+       jmp     @r0
+movmem_done: ! share slot insn, works out aligned.
+       lds.l   @r15+,pr
+       mov.l   r0,@(56,r4)
+       mov.l   @(52,r5),r0
+       rts
+       mov.l   r0,@(52,r4)
+       .balign 4
+
+       .global __movmemSI64
+       .global __movstrSI64
+       .set    __movstrSI64, __movmemSI64
+__movmemSI64:
+       mov.l   @(60,r5),r0
+       mov.l   r0,@(60,r4)
+       .global __movmemSI60
+       .global __movstrSI60
+       .set    __movstrSI60, __movmemSI60
+__movmemSI60:
+       mov.l   @(56,r5),r0
+       mov.l   r0,@(56,r4)
+       .global __movmemSI56
+       .global __movstrSI56
+       .set    __movstrSI56, __movmemSI56
+__movmemSI56:
+       mov.l   @(52,r5),r0
+       mov.l   r0,@(52,r4)
+       .global __movmemSI52
+       .global __movstrSI52
+       .set    __movstrSI52, __movmemSI52
+__movmemSI52:
+       mov.l   @(48,r5),r0
+       mov.l   r0,@(48,r4)
+       .global __movmemSI48
+       .global __movstrSI48
+       .set    __movstrSI48, __movmemSI48
+__movmemSI48:
+       mov.l   @(44,r5),r0
+       mov.l   r0,@(44,r4)
+       .global __movmemSI44
+       .global __movstrSI44
+       .set    __movstrSI44, __movmemSI44
+__movmemSI44:
+       mov.l   @(40,r5),r0
+       mov.l   r0,@(40,r4)
+       .global __movmemSI40
+       .global __movstrSI40
+       .set    __movstrSI40, __movmemSI40
+__movmemSI40:
+       mov.l   @(36,r5),r0
+       mov.l   r0,@(36,r4)
+       .global __movmemSI36
+       .global __movstrSI36
+       .set    __movstrSI36, __movmemSI36
+__movmemSI36:
+       mov.l   @(32,r5),r0
+       mov.l   r0,@(32,r4)
+       .global __movmemSI32
+       .global __movstrSI32
+       .set    __movstrSI32, __movmemSI32
+__movmemSI32:
+       mov.l   @(28,r5),r0
+       mov.l   r0,@(28,r4)
+       .global __movmemSI28
+       .global __movstrSI28
+       .set    __movstrSI28, __movmemSI28
+__movmemSI28:
+       mov.l   @(24,r5),r0
+       mov.l   r0,@(24,r4)
+       .global __movmemSI24
+       .global __movstrSI24
+       .set    __movstrSI24, __movmemSI24
+__movmemSI24:
+       mov.l   @(20,r5),r0
+       mov.l   r0,@(20,r4)
+       .global __movmemSI20
+       .global __movstrSI20
+       .set    __movstrSI20, __movmemSI20
+__movmemSI20:
+       mov.l   @(16,r5),r0
+       mov.l   r0,@(16,r4)
+       .global __movmemSI16
+       .global __movstrSI16
+       .set    __movstrSI16, __movmemSI16
+__movmemSI16:
+       mov.l   @(12,r5),r0
+       mov.l   r0,@(12,r4)
+       .global __movmemSI12
+       .global __movstrSI12
+       .set    __movstrSI12, __movmemSI12
+__movmemSI12:
+       mov.l   @(8,r5),r0
+       mov.l   r0,@(8,r4)
+       .global __movmemSI8
+       .global __movstrSI8
+       .set    __movstrSI8, __movmemSI8
+__movmemSI8:
+       mov.l   @(4,r5),r0
+       mov.l   r0,@(4,r4)
+       .global __movmemSI4
+       .global __movstrSI4
+       .set    __movstrSI4, __movmemSI4
+__movmemSI4:
+       mov.l   @(0,r5),r0
+       rts
+       mov.l   r0,@(0,r4)
+
+       .global __movmem_i4_even
+       .global __movstr_i4_even
+       .set    __movstr_i4_even, __movmem_i4_even
+
+       .global __movmem_i4_odd
+       .global __movstr_i4_odd
+       .set    __movstr_i4_odd, __movmem_i4_odd
+
+       .global __movmemSI12_i4
+       .global __movstrSI12_i4
+       .set    __movstrSI12_i4, __movmemSI12_i4
+
+       .p2align        5
+L_movmem_2mod4_end:
+       mov.l   r0,@(16,r4)
+       rts
+       mov.l   r1,@(20,r4)
+
+       .p2align        2
+
+__movmem_i4_even:
+       mov.l   @r5+,r0
+       bra     L_movmem_start_even
+       mov.l   @r5+,r1
+
+__movmem_i4_odd:
+       mov.l   @r5+,r1
+       add     #-4,r4
+       mov.l   @r5+,r2
+       mov.l   @r5+,r3
+       mov.l   r1,@(4,r4)
+       mov.l   r2,@(8,r4)
+
+L_movmem_loop:
+       mov.l   r3,@(12,r4)
+       dt      r6
+       mov.l   @r5+,r0
+       bt/s    L_movmem_2mod4_end
+       mov.l   @r5+,r1
+       add     #16,r4
+L_movmem_start_even:
+       mov.l   @r5+,r2
+       mov.l   @r5+,r3
+       mov.l   r0,@r4
+       dt      r6
+       mov.l   r1,@(4,r4)
+       bf/s    L_movmem_loop
+       mov.l   r2,@(8,r4)
+       rts
+       mov.l   r3,@(12,r4)
+
+       .p2align        4
+__movmemSI12_i4:
+       mov.l   @r5,r0
+       mov.l   @(4,r5),r1
+       mov.l   @(8,r5),r2
+       mov.l   r0,@r4
+       mov.l   r1,@(4,r4)
+       rts
+       mov.l   r2,@(8,r4)
diff --git a/arch/sh/lib/udiv_qrnnd.S b/arch/sh/lib/udiv_qrnnd.S
new file mode 100644 (file)
index 0000000..32b9a36
--- /dev/null
@@ -0,0 +1,81 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+   2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file 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; see the file COPYING.  If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
+!! recoded in assembly by Toshiyasu Morita
+!! tm@netcom.com
+
+/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
+   ELF local label prefixes by J"orn Rennecke
+   amylaar@cygnus.com  */
+
+       /* r0: rn r1: qn */ /* r0: n1 r4: n0 r5: d r6: d1 */ /* r2: __m */
+       /* n1 < d, but n1 might be larger than d1.  */
+       .global __udiv_qrnnd_16
+       .balign 8
+__udiv_qrnnd_16:
+       div0u
+       cmp/hi r6,r0
+       bt .Lots
+       .rept 16
+       div1 r6,r0 
+       .endr
+       extu.w r0,r1
+       bt 0f
+       add r6,r0
+0:     rotcl r1
+       mulu.w r1,r5
+       xtrct r4,r0
+       swap.w r0,r0
+       sts macl,r2
+       cmp/hs r2,r0
+       sub r2,r0
+       bt 0f
+       addc r5,r0
+       add #-1,r1
+       bt 0f
+1:     add #-1,r1
+       rts
+       add r5,r0
+       .balign 8
+.Lots:
+       sub r5,r0
+       swap.w r4,r1
+       xtrct r0,r1
+       clrt
+       mov r1,r0
+       addc r5,r0
+       mov #-1,r1
+       bf/s 1b
+        shlr16 r1
+0:     rts
+        nop
diff --git a/arch/sh/lib/udivsi3.S b/arch/sh/lib/udivsi3.S
new file mode 100644 (file)
index 0000000..72157ab
--- /dev/null
@@ -0,0 +1,87 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+   2004, 2005
+   Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file 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; see the file COPYING.  If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+       .balign 4
+       .global __udivsi3
+       .type   __udivsi3, @function
+div8:
+       div1 r5,r4
+div7:
+       div1 r5,r4; div1 r5,r4; div1 r5,r4
+       div1 r5,r4; div1 r5,r4; div1 r5,r4; rts; div1 r5,r4
+
+divx4:
+       div1 r5,r4; rotcl r0
+       div1 r5,r4; rotcl r0
+       div1 r5,r4; rotcl r0
+       rts; div1 r5,r4
+
+__udivsi3:
+       sts.l pr,@-r15
+       extu.w r5,r0
+       cmp/eq r5,r0
+       bf/s large_divisor
+       div0u
+       swap.w r4,r0
+       shlr16 r4
+       bsr div8
+       shll16 r5
+       bsr div7
+       div1 r5,r4
+       xtrct r4,r0
+       xtrct r0,r4
+       bsr div8
+       swap.w r4,r4
+       bsr div7
+       div1 r5,r4
+       lds.l @r15+,pr
+       xtrct r4,r0
+       swap.w r0,r0
+       rotcl r0
+       rts
+       shlr16 r5
+
+large_divisor:
+       mov #0,r0
+       xtrct r4,r0
+       xtrct r0,r4
+       bsr divx4
+       rotcl r0
+       bsr divx4
+       rotcl r0
+       bsr divx4
+       rotcl r0
+       bsr divx4
+       rotcl r0
+       lds.l @r15+,pr
+       rts
+       rotcl r0
diff --git a/arch/sh/lib/udivsi3_i4i-Os.S b/arch/sh/lib/udivsi3_i4i-Os.S
new file mode 100644 (file)
index 0000000..4835553
--- /dev/null
@@ -0,0 +1,149 @@
+/* Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file 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; see the file COPYING.  If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
+
+/* Moderately Space-optimized libgcc routines for the Renesas SH /
+   STMicroelectronics ST40 CPUs.
+   Contributed by J"orn Rennecke joern.rennecke@st.com.  */
+
+/* Size: 186 bytes jointly for udivsi3_i4i and sdivsi3_i4i
+   sh4-200 run times:
+   udiv small divisor: 55 cycles
+   udiv large divisor: 52 cycles
+   sdiv small divisor, positive result: 59 cycles
+   sdiv large divisor, positive result: 56 cycles
+   sdiv small divisor, negative result: 65 cycles (*)
+   sdiv large divisor, negative result: 62 cycles (*)
+   (*): r2 is restored in the rts delay slot and has a lingering latency
+        of two more cycles.  */
+       .balign 4
+       .global __udivsi3_i4i
+       .global __udivsi3_i4
+       .set    __udivsi3_i4, __udivsi3_i4i
+       .type   __udivsi3_i4i, @function
+       .type   __sdivsi3_i4i, @function
+__udivsi3_i4i:
+       sts pr,r1
+       mov.l r4,@-r15
+       extu.w r5,r0
+       cmp/eq r5,r0
+       swap.w r4,r0
+       shlr16 r4
+       bf/s large_divisor
+       div0u
+       mov.l r5,@-r15
+       shll16 r5
+sdiv_small_divisor:
+       div1 r5,r4
+       bsr div6
+       div1 r5,r4
+       div1 r5,r4
+       bsr div6
+       div1 r5,r4
+       xtrct r4,r0
+       xtrct r0,r4
+       bsr div7
+       swap.w r4,r4
+       div1 r5,r4
+       bsr div7
+       div1 r5,r4
+       xtrct r4,r0
+       mov.l @r15+,r5
+       swap.w r0,r0
+       mov.l @r15+,r4
+       jmp @r1
+       rotcl r0
+div7:
+       div1 r5,r4
+div6:
+                   div1 r5,r4; div1 r5,r4; div1 r5,r4
+       div1 r5,r4; div1 r5,r4; rts;        div1 r5,r4
+
+divx3:
+       rotcl r0
+       div1 r5,r4
+       rotcl r0
+       div1 r5,r4
+       rotcl r0
+       rts
+       div1 r5,r4
+
+large_divisor:
+       mov.l r5,@-r15
+sdiv_large_divisor:
+       xor r4,r0
+       .rept 4
+       rotcl r0
+       bsr divx3
+       div1 r5,r4
+       .endr
+       mov.l @r15+,r5
+       mov.l @r15+,r4
+       jmp @r1
+       rotcl r0
+
+       .global __sdivsi3_i4i
+       .global __sdivsi3_i4
+       .global __sdivsi3
+       .set    __sdivsi3_i4, __sdivsi3_i4i
+       .set    __sdivsi3, __sdivsi3_i4i
+__sdivsi3_i4i:
+       mov.l r4,@-r15
+       cmp/pz r5
+       mov.l r5,@-r15
+       bt/s pos_divisor
+       cmp/pz r4
+       neg r5,r5
+       extu.w r5,r0
+       bt/s neg_result
+       cmp/eq r5,r0
+       neg r4,r4
+pos_result:
+       swap.w r4,r0
+       bra sdiv_check_divisor
+       sts pr,r1
+pos_divisor:
+       extu.w r5,r0
+       bt/s pos_result
+       cmp/eq r5,r0
+       neg r4,r4
+neg_result:
+       mova negate_result,r0
+       ;
+       mov r0,r1
+       swap.w r4,r0
+       lds r2,macl
+       sts pr,r2
+sdiv_check_divisor:
+       shlr16 r4
+       bf/s sdiv_large_divisor
+       div0u
+       bra sdiv_small_divisor
+       shll16 r5
+       .balign 4
+negate_result:
+       neg r0,r0
+       jmp @r2
+       sts macl,r2
diff --git a/arch/sh/lib/udivsi3_i4i.S b/arch/sh/lib/udivsi3_i4i.S
new file mode 100644 (file)
index 0000000..f1a79d9
--- /dev/null
@@ -0,0 +1,666 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+   2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file 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; see the file COPYING.  If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
+!! recoded in assembly by Toshiyasu Morita
+!! tm@netcom.com
+
+/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
+   ELF local label prefixes by J"orn Rennecke
+   amylaar@cygnus.com  */
+
+/* This code used shld, thus is not suitable for SH1 / SH2.  */
+
+/* Signed / unsigned division without use of FPU, optimized for SH4.
+   Uses a lookup table for divisors in the range -128 .. +128, and
+   div1 with case distinction for larger divisors in three more ranges.
+   The code is lumped together with the table to allow the use of mova.  */
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+#define L_LSB 0
+#define L_LSWMSB 1
+#define L_MSWLSB 2
+#else
+#define L_LSB 3
+#define L_LSWMSB 2
+#define L_MSWLSB 1
+#endif
+
+       .balign 4
+       .global __udivsi3_i4i
+       .global __udivsi3_i4
+       .set    __udivsi3_i4, __udivsi3_i4i
+       .type   __udivsi3_i4i, @function
+__udivsi3_i4i:
+       mov.w c128_w, r1
+       div0u
+       mov r4,r0
+       shlr8 r0
+       cmp/hi r1,r5
+       extu.w r5,r1
+       bf udiv_le128
+       cmp/eq r5,r1
+       bf udiv_ge64k
+       shlr r0
+       mov r5,r1
+       shll16 r5
+       mov.l r4,@-r15
+       div1 r5,r0
+       mov.l r1,@-r15
+       div1 r5,r0
+       div1 r5,r0
+       bra udiv_25
+       div1 r5,r0
+
+div_le128:
+       mova div_table_ix,r0
+       bra div_le128_2
+       mov.b @(r0,r5),r1
+udiv_le128:
+       mov.l r4,@-r15
+       mova div_table_ix,r0
+       mov.b @(r0,r5),r1
+       mov.l r5,@-r15
+div_le128_2:
+       mova div_table_inv,r0
+       mov.l @(r0,r1),r1
+       mov r5,r0
+       tst #0xfe,r0
+       mova div_table_clz,r0
+       dmulu.l r1,r4
+       mov.b @(r0,r5),r1
+       bt/s div_by_1
+       mov r4,r0
+       mov.l @r15+,r5
+       sts mach,r0
+       /* clrt */
+       addc r4,r0
+       mov.l @r15+,r4
+       rotcr r0
+       rts
+       shld r1,r0
+
+div_by_1_neg:
+       neg r4,r0
+div_by_1:
+       mov.l @r15+,r5
+       rts
+       mov.l @r15+,r4
+
+div_ge64k:
+       bt/s div_r8
+       div0u
+       shll8 r5
+       bra div_ge64k_2
+       div1 r5,r0
+udiv_ge64k:
+       cmp/hi r0,r5
+       mov r5,r1
+       bt udiv_r8
+       shll8 r5
+       mov.l r4,@-r15
+       div1 r5,r0
+       mov.l r1,@-r15
+div_ge64k_2:
+       div1 r5,r0
+       mov.l zero_l,r1
+       .rept 4
+       div1 r5,r0
+       .endr
+       mov.l r1,@-r15
+       div1 r5,r0
+       mov.w m256_w,r1
+       div1 r5,r0
+       mov.b r0,@(L_LSWMSB,r15)
+       xor r4,r0
+       and r1,r0
+       bra div_ge64k_end
+       xor r4,r0
+       
+div_r8:
+       shll16 r4
+       bra div_r8_2
+       shll8 r4
+udiv_r8:
+       mov.l r4,@-r15
+       shll16 r4
+       clrt
+       shll8 r4
+       mov.l r5,@-r15
+div_r8_2:
+       rotcl r4
+       mov r0,r1
+       div1 r5,r1
+       mov r4,r0
+       rotcl r0
+       mov r5,r4
+       div1 r5,r1
+       .rept 5
+       rotcl r0; div1 r5,r1
+       .endr
+       rotcl r0
+       mov.l @r15+,r5
+       div1 r4,r1
+       mov.l @r15+,r4
+       rts
+       rotcl r0
+
+       .global __sdivsi3_i4i
+       .global __sdivsi3_i4
+       .global __sdivsi3
+       .set    __sdivsi3_i4, __sdivsi3_i4i
+       .set    __sdivsi3, __sdivsi3_i4i
+       .type   __sdivsi3_i4i, @function
+       /* This is link-compatible with a __sdivsi3 call,
+          but we effectively clobber only r1.  */
+__sdivsi3_i4i:
+       mov.l r4,@-r15
+       cmp/pz r5
+       mov.w c128_w, r1
+       bt/s pos_divisor
+       cmp/pz r4
+       mov.l r5,@-r15
+       neg r5,r5
+       bt/s neg_result
+       cmp/hi r1,r5
+       neg r4,r4
+pos_result:
+       extu.w r5,r0
+       bf div_le128
+       cmp/eq r5,r0
+       mov r4,r0
+       shlr8 r0
+       bf/s div_ge64k
+       cmp/hi r0,r5
+       div0u
+       shll16 r5
+       div1 r5,r0
+       div1 r5,r0
+       div1 r5,r0
+udiv_25:
+       mov.l zero_l,r1
+       div1 r5,r0
+       div1 r5,r0
+       mov.l r1,@-r15
+       .rept 3
+       div1 r5,r0
+       .endr
+       mov.b r0,@(L_MSWLSB,r15)
+       xtrct r4,r0
+       swap.w r0,r0
+       .rept 8
+       div1 r5,r0
+       .endr
+       mov.b r0,@(L_LSWMSB,r15)
+div_ge64k_end:
+       .rept 8
+       div1 r5,r0
+       .endr
+       mov.l @r15+,r4 ! zero-extension and swap using LS unit.
+       extu.b r0,r0
+       mov.l @r15+,r5
+       or r4,r0
+       mov.l @r15+,r4
+       rts
+       rotcl r0
+
+div_le128_neg:
+       tst #0xfe,r0
+       mova div_table_ix,r0
+       mov.b @(r0,r5),r1
+       mova div_table_inv,r0
+       bt/s div_by_1_neg
+       mov.l @(r0,r1),r1
+       mova div_table_clz,r0
+       dmulu.l r1,r4
+       mov.b @(r0,r5),r1
+       mov.l @r15+,r5
+       sts mach,r0
+       /* clrt */
+       addc r4,r0
+       mov.l @r15+,r4
+       rotcr r0
+       shld r1,r0
+       rts
+       neg r0,r0
+
+pos_divisor:
+       mov.l r5,@-r15
+       bt/s pos_result
+       cmp/hi r1,r5
+       neg r4,r4
+neg_result:
+       extu.w r5,r0
+       bf div_le128_neg
+       cmp/eq r5,r0
+       mov r4,r0
+       shlr8 r0
+       bf/s div_ge64k_neg
+       cmp/hi r0,r5
+       div0u
+       mov.l zero_l,r1
+       shll16 r5
+       div1 r5,r0
+       mov.l r1,@-r15
+       .rept 7
+       div1 r5,r0
+       .endr
+       mov.b r0,@(L_MSWLSB,r15)
+       xtrct r4,r0
+       swap.w r0,r0
+       .rept 8
+       div1 r5,r0
+       .endr
+       mov.b r0,@(L_LSWMSB,r15)
+div_ge64k_neg_end:
+       .rept 8
+       div1 r5,r0
+       .endr
+       mov.l @r15+,r4 ! zero-extension and swap using LS unit.
+       extu.b r0,r1
+       mov.l @r15+,r5
+       or r4,r1
+div_r8_neg_end:
+       mov.l @r15+,r4
+       rotcl r1
+       rts
+       neg r1,r0
+
+div_ge64k_neg:
+       bt/s div_r8_neg
+       div0u
+       shll8 r5
+       mov.l zero_l,r1
+       .rept 6
+       div1 r5,r0
+       .endr
+       mov.l r1,@-r15
+       div1 r5,r0
+       mov.w m256_w,r1
+       div1 r5,r0
+       mov.b r0,@(L_LSWMSB,r15)
+       xor r4,r0
+       and r1,r0
+       bra div_ge64k_neg_end
+       xor r4,r0
+
+c128_w:
+       .word 128
+
+div_r8_neg:
+       clrt
+       shll16 r4
+       mov r4,r1
+       shll8 r1
+       mov r5,r4
+       .rept 7
+       rotcl r1; div1 r5,r0
+       .endr
+       mov.l @r15+,r5
+       rotcl r1
+       bra div_r8_neg_end
+       div1 r4,r0
+
+m256_w:
+       .word 0xff00
+/* This table has been generated by divtab-sh4.c.  */
+       .balign 4
+div_table_clz:
+       .byte   0
+       .byte   1
+       .byte   0
+       .byte   -1
+       .byte   -1
+       .byte   -2
+       .byte   -2
+       .byte   -2
+       .byte   -2
+       .byte   -3
+       .byte   -3
+       .byte   -3
+       .byte   -3
+       .byte   -3
+       .byte   -3
+       .byte   -3
+       .byte   -3
+       .byte   -4
+       .byte   -4
+       .byte   -4
+       .byte   -4
+       .byte   -4
+       .byte   -4
+       .byte   -4
+       .byte   -4
+       .byte   -4
+       .byte   -4
+       .byte   -4
+       .byte   -4
+       .byte   -4
+       .byte   -4
+       .byte   -4
+       .byte   -4
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -5
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+       .byte   -6
+/* Lookup table translating positive divisor to index into table of
+   normalized inverse.  N.B. the '0' entry is also the last entry of the
+ previous table, and causes an unaligned access for division by zero.  */
+div_table_ix:
+       .byte   -6
+       .byte   -128
+       .byte   -128
+       .byte   0
+       .byte   -128
+       .byte   -64
+       .byte   0
+       .byte   64
+       .byte   -128
+       .byte   -96
+       .byte   -64
+       .byte   -32
+       .byte   0
+       .byte   32
+       .byte   64
+       .byte   96
+       .byte   -128
+       .byte   -112
+       .byte   -96
+       .byte   -80
+       .byte   -64
+       .byte   -48
+       .byte   -32
+       .byte   -16
+       .byte   0
+       .byte   16
+       .byte   32
+       .byte   48
+       .byte   64
+       .byte   80
+       .byte   96
+       .byte   112
+       .byte   -128
+       .byte   -120
+       .byte   -112
+       .byte   -104
+       .byte   -96
+       .byte   -88
+       .byte   -80
+       .byte   -72
+       .byte   -64
+       .byte   -56
+       .byte   -48
+       .byte   -40
+       .byte   -32
+       .byte   -24
+       .byte   -16
+       .byte   -8
+       .byte   0
+       .byte   8
+       .byte   16
+       .byte   24
+       .byte   32
+       .byte   40
+       .byte   48
+       .byte   56
+       .byte   64
+       .byte   72
+       .byte   80
+       .byte   88
+       .byte   96
+       .byte   104
+       .byte   112
+       .byte   120
+       .byte   -128
+       .byte   -124
+       .byte   -120
+       .byte   -116
+       .byte   -112
+       .byte   -108
+       .byte   -104
+       .byte   -100
+       .byte   -96
+       .byte   -92
+       .byte   -88
+       .byte   -84
+       .byte   -80
+       .byte   -76
+       .byte   -72
+       .byte   -68
+       .byte   -64
+       .byte   -60
+       .byte   -56
+       .byte   -52
+       .byte   -48
+       .byte   -44
+       .byte   -40
+       .byte   -36
+       .byte   -32
+       .byte   -28
+       .byte   -24
+       .byte   -20
+       .byte   -16
+       .byte   -12
+       .byte   -8
+       .byte   -4
+       .byte   0
+       .byte   4
+       .byte   8
+       .byte   12
+       .byte   16
+       .byte   20
+       .byte   24
+       .byte   28
+       .byte   32
+       .byte   36
+       .byte   40
+       .byte   44
+       .byte   48
+       .byte   52
+       .byte   56
+       .byte   60
+       .byte   64
+       .byte   68
+       .byte   72
+       .byte   76
+       .byte   80
+       .byte   84
+       .byte   88
+       .byte   92
+       .byte   96
+       .byte   100
+       .byte   104
+       .byte   108
+       .byte   112
+       .byte   116
+       .byte   120
+       .byte   124
+       .byte   -128
+/* 1/64 .. 1/127, normalized.  There is an implicit leading 1 in bit 32.  */
+       .balign 4
+zero_l:
+       .long   0x0
+       .long   0xF81F81F9
+       .long   0xF07C1F08
+       .long   0xE9131AC0
+       .long   0xE1E1E1E2
+       .long   0xDAE6076C
+       .long   0xD41D41D5
+       .long   0xCD856891
+       .long   0xC71C71C8
+       .long   0xC0E07039
+       .long   0xBACF914D
+       .long   0xB4E81B4F
+       .long   0xAF286BCB
+       .long   0xA98EF607
+       .long   0xA41A41A5
+       .long   0x9EC8E952
+       .long   0x9999999A
+       .long   0x948B0FCE
+       .long   0x8F9C18FA
+       .long   0x8ACB90F7
+       .long   0x86186187
+       .long   0x81818182
+       .long   0x7D05F418
+       .long   0x78A4C818
+       .long   0x745D1746
+       .long   0x702E05C1
+       .long   0x6C16C16D
+       .long   0x68168169
+       .long   0x642C8591
+       .long   0x60581606
+       .long   0x5C9882BA
+       .long   0x58ED2309
+div_table_inv:
+       .long   0x55555556
+       .long   0x51D07EAF
+       .long   0x4E5E0A73
+       .long   0x4AFD6A06
+       .long   0x47AE147B
+       .long   0x446F8657
+       .long   0x41414142
+       .long   0x3E22CBCF
+       .long   0x3B13B13C
+       .long   0x38138139
+       .long   0x3521CFB3
+       .long   0x323E34A3
+       .long   0x2F684BDB
+       .long   0x2C9FB4D9
+       .long   0x29E4129F
+       .long   0x27350B89
+       .long   0x24924925
+       .long   0x21FB7813
+       .long   0x1F7047DD
+       .long   0x1CF06ADB
+       .long   0x1A7B9612
+       .long   0x18118119
+       .long   0x15B1E5F8
+       .long   0x135C8114
+       .long   0x11111112
+       .long   0xECF56BF
+       .long   0xC9714FC
+       .long   0xA6810A7
+       .long   0x8421085
+       .long   0x624DD30
+       .long   0x4104105
+       .long   0x2040811
+       /* maximum error: 0.987342 scaled: 0.921875*/
index 9950966..4bacb9e 100644 (file)
@@ -2,7 +2,7 @@
 # Makefile for the SH-5 specific library files..
 #
 # Copyright (C) 2000, 2001  Paolo Alberelli
-# Copyright (C) 2003  Paul Mundt
+# Copyright (C) 2003 - 2008  Paul Mundt
 #
 # This file is subject to the terms and conditions of the GNU General Public
 # License.  See the file "COPYING" in the main directory of this archive
@@ -10,6 +10,8 @@
 #
 
 # Panic should really be compiled as PIC
-lib-y  := udelay.o c-checksum.o dbg.o panic.o memcpy.o copy_user_memcpy.o \
-               copy_page.o clear_page.o
+lib-y  := udelay.o c-checksum.o dbg.o panic.o memcpy.o memset.o \
+         copy_user_memcpy.o copy_page.o clear_page.o strcpy.o strlen.o
 
+# Extracted from libgcc
+lib-y  += udivsi3.o udivdi3.o sdivsi3.o
index 5c284e0..73c0877 100644 (file)
@@ -35,7 +35,7 @@ static inline unsigned short foldto16(unsigned long x)
 
 static inline unsigned short myfoldto16(unsigned long long x)
 {
-       /* Fold down to 32-bits so we don't loose in the typedef-less
+       /* Fold down to 32-bits so we don't lose in the typedef-less
           network stack.  */
        /* 64 to 33 */
        x = (x & 0xffffffff) + (x >> 32);
@@ -199,7 +199,7 @@ __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
        result = (__force u64) saddr + (__force u64) daddr +
                 (__force u64) sum + ((len + proto) << 8);
 
-       /* Fold down to 32-bits so we don't loose in the typedef-less
+       /* Fold down to 32-bits so we don't lose in the typedef-less
           network stack.  */
        /* 64 to 33 */
        result = (result & 0xffffffff) + (result >> 32);
diff --git a/arch/sh/lib64/memcpy.S b/arch/sh/lib64/memcpy.S
new file mode 100644 (file)
index 0000000..dd300c3
--- /dev/null
@@ -0,0 +1,201 @@
+/* Cloned and hacked for uClibc by Paul Mundt, December 2003 */
+/* Modified by SuperH, Inc. September 2003 */
+!
+! Fast SH memcpy
+!
+! by Toshiyasu Morita (tm@netcom.com)
+! hacked by J"orn Rernnecke (joern.rennecke@superh.com) ("o for o-umlaut)
+! SH5 code Copyright 2002 SuperH Ltd.
+!
+! Entry: ARG0: destination pointer
+!        ARG1: source pointer
+!        ARG2: byte count
+!
+! Exit:  RESULT: destination pointer
+!        any other registers in the range r0-r7: trashed
+!
+! Notes: Usually one wants to do small reads and write a longword, but
+!        unfortunately it is difficult in some cases to concatanate bytes
+!        into a longword on the SH, so this does a longword read and small
+!        writes.
+!
+! This implementation makes two assumptions about how it is called:
+!
+! 1.: If the byte count is nonzero, the address of the last byte to be
+!     copied is unsigned greater than the address of the first byte to
+!     be copied.  This could be easily swapped for a signed comparison,
+!     but the algorithm used needs some comparison.
+!
+! 2.: When there are two or three bytes in the last word of an 11-or-more
+!     bytes memory chunk to b copied, the rest of the word can be read
+!     without side effects.
+!     This could be easily changed by increasing the minumum size of
+!     a fast memcpy and the amount subtracted from r7 before L_2l_loop be 2,
+!     however, this would cost a few extra cyles on average.
+!     For SHmedia, the assumption is that any quadword can be read in its
+!     enirety if at least one byte is included in the copy.
+!
+
+       .section .text..SHmedia32,"ax"
+       .globl  memcpy
+       .type   memcpy, @function
+       .align  5
+
+memcpy:
+
+#define LDUAQ(P,O,D0,D1) ldlo.q P,O,D0; ldhi.q P,O+7,D1
+#define STUAQ(P,O,D0,D1) stlo.q P,O,D0; sthi.q P,O+7,D1
+#define LDUAL(P,O,D0,D1) ldlo.l P,O,D0; ldhi.l P,O+3,D1
+#define STUAL(P,O,D0,D1) stlo.l P,O,D0; sthi.l P,O+3,D1
+
+       ld.b r3,0,r63
+       pta/l Large,tr0
+       movi 25,r0
+       bgeu/u r4,r0,tr0
+       nsb r4,r0
+       shlli r0,5,r0
+       movi (L1-L0+63*32 + 1) & 0xffff,r1
+       sub r1, r0, r0
+L0:    ptrel r0,tr0
+       add r2,r4,r5
+       ptabs r18,tr1
+       add r3,r4,r6
+       blink tr0,r63
+       
+/* Rearranged to make cut2 safe */
+       .balign 8
+L4_7:  /* 4..7 byte memcpy cntd. */
+       stlo.l r2, 0, r0
+       or r6, r7, r6
+       sthi.l r5, -1, r6
+       stlo.l r5, -4, r6
+       blink tr1,r63
+
+       .balign 8
+L1:    /* 0 byte memcpy */
+       nop
+       blink tr1,r63
+       nop
+       nop
+       nop
+       nop
+
+L2_3:  /* 2 or 3 byte memcpy cntd. */
+       st.b r5,-1,r6
+       blink tr1,r63
+
+       /* 1 byte memcpy */
+       ld.b r3,0,r0
+       st.b r2,0,r0
+       blink tr1,r63
+
+L8_15: /* 8..15 byte memcpy cntd. */
+       stlo.q r2, 0, r0
+       or r6, r7, r6
+       sthi.q r5, -1, r6
+       stlo.q r5, -8, r6
+       blink tr1,r63
+       
+       /* 2 or 3 byte memcpy */
+       ld.b r3,0,r0
+       ld.b r2,0,r63
+       ld.b r3,1,r1
+       st.b r2,0,r0
+       pta/l L2_3,tr0
+       ld.b r6,-1,r6
+       st.b r2,1,r1
+       blink tr0, r63
+
+       /* 4 .. 7 byte memcpy */
+       LDUAL (r3, 0, r0, r1)
+       pta L4_7, tr0
+       ldlo.l r6, -4, r7
+       or r0, r1, r0
+       sthi.l r2, 3, r0
+       ldhi.l r6, -1, r6
+       blink tr0, r63
+
+       /* 8 .. 15 byte memcpy */
+       LDUAQ (r3, 0, r0, r1)
+       pta L8_15, tr0
+       ldlo.q r6, -8, r7
+       or r0, r1, r0
+       sthi.q r2, 7, r0
+       ldhi.q r6, -1, r6
+       blink tr0, r63
+
+       /* 16 .. 24 byte memcpy */
+       LDUAQ (r3, 0, r0, r1)
+       LDUAQ (r3, 8, r8, r9)
+       or r0, r1, r0
+       sthi.q r2, 7, r0
+       or r8, r9, r8
+       sthi.q r2, 15, r8
+       ldlo.q r6, -8, r7
+       ldhi.q r6, -1, r6
+       stlo.q r2, 8, r8
+       stlo.q r2, 0, r0
+       or r6, r7, r6
+       sthi.q r5, -1, r6
+       stlo.q r5, -8, r6
+       blink tr1,r63
+
+Large:
+       ld.b r2, 0, r63
+       pta/l  Loop_ua, tr1
+       ori r3, -8, r7
+       sub r2, r7, r22
+       sub r3, r2, r6
+       add r2, r4, r5
+       ldlo.q r3, 0, r0
+       addi r5, -16, r5
+       movi 64+8, r27 // could subtract r7 from that.
+       stlo.q r2, 0, r0
+       sthi.q r2, 7, r0
+       ldx.q r22, r6, r0
+       bgtu/l r27, r4, tr1
+
+       addi r5, -48, r27
+       pta/l Loop_line, tr0
+       addi r6, 64, r36
+       addi r6, -24, r19
+       addi r6, -16, r20
+       addi r6, -8, r21
+
+Loop_line:
+       ldx.q r22, r36, r63
+       alloco r22, 32
+       addi r22, 32, r22
+       ldx.q r22, r19, r23
+       sthi.q r22, -25, r0
+       ldx.q r22, r20, r24
+       ldx.q r22, r21, r25
+       stlo.q r22, -32, r0
+       ldx.q r22, r6,  r0
+       sthi.q r22, -17, r23
+       sthi.q r22,  -9, r24
+       sthi.q r22,  -1, r25
+       stlo.q r22, -24, r23
+       stlo.q r22, -16, r24
+       stlo.q r22,  -8, r25
+       bgeu r27, r22, tr0
+
+Loop_ua:
+       addi r22, 8, r22
+       sthi.q r22, -1, r0
+       stlo.q r22, -8, r0
+       ldx.q r22, r6, r0
+       bgtu/l r5, r22, tr1
+
+       add r3, r4, r7
+       ldlo.q r7, -8, r1
+       sthi.q r22, 7, r0
+       ldhi.q r7, -1, r7
+       ptabs r18,tr1
+       stlo.q r22, 0, r0
+       or r1, r7, r1
+       sthi.q r5, 15, r1
+       stlo.q r5, 8, r1
+       blink tr1, r63
+
+       .size memcpy,.-memcpy
diff --git a/arch/sh/lib64/memcpy.c b/arch/sh/lib64/memcpy.c
deleted file mode 100644 (file)
index fba436a..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2002 Mark Debbage (Mark.Debbage@superh.com)
- *
- * May be copied or modified under the terms of the GNU General Public
- * License.  See linux/COPYING for more information.
- *
- */
-
-#include <linux/types.h>
-#include <asm/string.h>
-
-// This is a simplistic optimization of memcpy to increase the
-// granularity of access beyond one byte using aligned
-// loads and stores. This is not an optimal implementation
-// for SH-5 (especially with regard to prefetching and the cache),
-// and a better version should be provided later ...
-
-void *memcpy(void *dest, const void *src, size_t count)
-{
-       char *d = (char *) dest, *s = (char *) src;
-
-       if (count >= 32) {
-               int i = 8 - (((unsigned long) d) & 0x7);
-
-               if (i != 8)
-                       while (i-- && count--) {
-                               *d++ = *s++;
-                       }
-
-               if (((((unsigned long) d) & 0x7) == 0) &&
-                   ((((unsigned long) s) & 0x7) == 0)) {
-                       while (count >= 32) {
-                               unsigned long long t1, t2, t3, t4;
-                               t1 = *(unsigned long long *) (s);
-                               t2 = *(unsigned long long *) (s + 8);
-                               t3 = *(unsigned long long *) (s + 16);
-                               t4 = *(unsigned long long *) (s + 24);
-                               *(unsigned long long *) (d) = t1;
-                               *(unsigned long long *) (d + 8) = t2;
-                               *(unsigned long long *) (d + 16) = t3;
-                               *(unsigned long long *) (d + 24) = t4;
-                               d += 32;
-                               s += 32;
-                               count -= 32;
-                       }
-                       while (count >= 8) {
-                               *(unsigned long long *) d =
-                                   *(unsigned long long *) s;
-                               d += 8;
-                               s += 8;
-                               count -= 8;
-                       }
-               }
-
-               if (((((unsigned long) d) & 0x3) == 0) &&
-                   ((((unsigned long) s) & 0x3) == 0)) {
-                       while (count >= 4) {
-                               *(unsigned long *) d = *(unsigned long *) s;
-                               d += 4;
-                               s += 4;
-                               count -= 4;
-                       }
-               }
-
-               if (((((unsigned long) d) & 0x1) == 0) &&
-                   ((((unsigned long) s) & 0x1) == 0)) {
-                       while (count >= 2) {
-                               *(unsigned short *) d = *(unsigned short *) s;
-                               d += 2;
-                               s += 2;
-                               count -= 2;
-                       }
-               }
-       }
-
-       while (count--) {
-               *d++ = *s++;
-       }
-
-       return d;
-}
diff --git a/arch/sh/lib64/memset.S b/arch/sh/lib64/memset.S
new file mode 100644 (file)
index 0000000..2d37b04
--- /dev/null
@@ -0,0 +1,91 @@
+/* Cloned and hacked for uClibc by Paul Mundt, December 2003 */
+/* Modified by SuperH, Inc. September 2003 */
+!
+! Fast SH memset
+!
+! by Toshiyasu Morita (tm@netcom.com)
+!
+! SH5 code by J"orn Rennecke (joern.rennecke@superh.com)
+! Copyright 2002 SuperH Ltd.
+!
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define SHHI shlld
+#define SHLO shlrd
+#else
+#define SHHI shlrd
+#define SHLO shlld
+#endif
+
+       .section .text..SHmedia32,"ax"
+       .globl  memset
+       .type   memset, @function
+
+       .align 5
+
+memset:
+       pta/l multiquad, tr0
+       andi r2, 7, r22
+       ptabs r18, tr2
+       mshflo.b r3,r3,r3
+       add r4, r22, r23
+       mperm.w r3, r63, r3     // Fill pattern now in every byte of r3
+
+       movi 8, r9
+       bgtu/u r23, r9, tr0 // multiquad
+
+       beqi/u r4, 0, tr2       // Return with size 0 - ensures no mem accesses
+       ldlo.q r2, 0, r7
+       shlli r4, 2, r4
+       movi -1, r8
+       SHHI r8, r4, r8
+       SHHI r8, r4, r8
+       mcmv r7, r8, r3
+       stlo.q r2, 0, r3
+       blink tr2, r63
+
+multiquad:
+       pta/l lastquad, tr0
+       stlo.q r2, 0, r3
+       shlri r23, 3, r24
+       add r2, r4, r5
+       beqi/u r24, 1, tr0 // lastquad
+       pta/l loop, tr1
+       sub r2, r22, r25
+       andi r5, -8, r20   // calculate end address and
+       addi r20, -7*8, r8 // loop end address; This might overflow, so we need
+                          // to use a different test before we start the loop
+       bge/u r24, r9, tr1 // loop
+       st.q r25, 8, r3
+       st.q r20, -8, r3
+       shlri r24, 1, r24
+       beqi/u r24, 1, tr0 // lastquad
+       st.q r25, 16, r3
+       st.q r20, -16, r3
+       beqi/u r24, 2, tr0 // lastquad
+       st.q r25, 24, r3
+       st.q r20, -24, r3
+lastquad:
+       sthi.q r5, -1, r3
+       blink tr2,r63
+
+loop:
+!!!    alloco r25, 32  // QQQ comment out for short-term fix to SHUK #3895.
+                       // QQQ commenting out is locically correct, but sub-optimal
+                       // QQQ Sean McGoogan - 4th April 2003.
+       st.q r25, 8, r3
+       st.q r25, 16, r3
+       st.q r25, 24, r3
+       st.q r25, 32, r3
+       addi r25, 32, r25
+       bgeu/l r8, r25, tr1 // loop
+
+       st.q r20, -40, r3
+       st.q r20, -32, r3
+       st.q r20, -24, r3
+       st.q r20, -16, r3
+       st.q r20, -8, r3
+       sthi.q r5, -1, r3
+       blink tr2,r63
+
+       .size   memset,.-memset
diff --git a/arch/sh/lib64/sdivsi3.S b/arch/sh/lib64/sdivsi3.S
new file mode 100644 (file)
index 0000000..6a800c6
--- /dev/null
@@ -0,0 +1,131 @@
+       .global __sdivsi3
+       .section        .text..SHmedia32,"ax"
+       .align  2
+
+       /* inputs: r4,r5 */
+       /* clobbered: r1,r18,r19,r20,r21,r25,tr0 */
+       /* result in r0 */
+__sdivsi3:
+       ptb __div_table,tr0
+
+       nsb r5, r1
+       shlld r5, r1, r25    /* normalize; [-2 ..1, 1..2) in s2.62 */
+       shari r25, 58, r21   /* extract 5(6) bit index (s2.4 with hole -1..1) */
+       /* bubble */
+       gettr tr0,r20
+       ldx.ub r20, r21, r19 /* u0.8 */
+       shari r25, 32, r25   /* normalize to s2.30 */
+       shlli r21, 1, r21
+       muls.l r25, r19, r19 /* s2.38 */
+       ldx.w r20, r21, r21  /* s2.14 */
+       ptabs r18, tr0
+       shari r19, 24, r19   /* truncate to s2.14 */
+       sub r21, r19, r19    /* some 11 bit inverse in s1.14 */
+       muls.l r19, r19, r21 /* u0.28 */
+       sub r63, r1, r1
+       addi r1, 92, r1
+       muls.l r25, r21, r18 /* s2.58 */
+       shlli r19, 45, r19   /* multiply by two and convert to s2.58 */
+       /* bubble */
+       sub r19, r18, r18
+       shari r18, 28, r18   /* some 22 bit inverse in s1.30 */
+       muls.l r18, r25, r0  /* s2.60 */
+       muls.l r18, r4, r25 /* s32.30 */
+       /* bubble */
+       shari r0, 16, r19   /* s-16.44 */
+       muls.l r19, r18, r19 /* s-16.74 */
+       shari r25, 63, r0
+       shari r4, 14, r18   /* s19.-14 */
+       shari r19, 30, r19   /* s-16.44 */
+       muls.l r19, r18, r19 /* s15.30 */
+       xor r21, r0, r21    /* You could also use the constant 1 << 27. */
+       add r21, r25, r21
+       sub r21, r19, r21
+       shard r21, r1, r21
+       sub r21, r0, r0
+       blink tr0, r63
+       
+/* This table has been generated by divtab.c .
+Defects for bias -330:
+   Max defect: 6.081536e-07 at -1.000000e+00
+   Min defect: 2.849516e-08 at 1.030651e+00
+   Max 2nd step defect: 9.606539e-12 at -1.000000e+00
+   Min 2nd step defect: 0.000000e+00 at 0.000000e+00
+   Defect at 1: 1.238659e-07
+   Defect at -2: 1.061708e-07 */
+
+       .balign 2
+       .type   __div_table,@object
+       .size   __div_table,128
+/* negative division constants */
+       .word   -16638
+       .word   -17135
+       .word   -17737
+       .word   -18433
+       .word   -19103
+       .word   -19751
+       .word   -20583
+       .word   -21383
+       .word   -22343
+       .word   -23353
+       .word   -24407
+       .word   -25582
+       .word   -26863
+       .word   -28382
+       .word   -29965
+       .word   -31800
+/* negative division factors */
+       .byte   66
+       .byte   70
+       .byte   75
+       .byte   81
+       .byte   87
+       .byte   93
+       .byte   101
+       .byte   109
+       .byte   119
+       .byte   130
+       .byte   142
+       .byte   156
+       .byte   172
+       .byte   192
+       .byte   214
+       .byte   241
+       .skip 16
+       .global __div_table
+__div_table:
+       .skip 16
+/* positive division factors */
+       .byte   241
+       .byte   214
+       .byte   192
+       .byte   172
+       .byte   156
+       .byte   142
+       .byte   130
+       .byte   119
+       .byte   109
+       .byte   101
+       .byte   93
+       .byte   87
+       .byte   81
+       .byte   75
+       .byte   70
+       .byte   66
+/* positive division constants */
+       .word   31801
+       .word   29966
+       .word   28383
+       .word   26864
+       .word   25583
+       .word   24408
+       .word   23354
+       .word   22344
+       .word   21384
+       .word   20584
+       .word   19752
+       .word   19104
+       .word   18434
+       .word   17738
+       .word   17136
+       .word   16639
diff --git a/arch/sh/lib64/strcpy.S b/arch/sh/lib64/strcpy.S
new file mode 100644 (file)
index 0000000..ea7c9c5
--- /dev/null
@@ -0,0 +1,97 @@
+/* Cloned and hacked for uClibc by Paul Mundt, December 2003 */
+/* Modified by SuperH, Inc. September 2003 */
+! Entry: arg0: destination
+!        arg1: source
+! Exit:  result: destination
+!
+! SH5 code Copyright 2002 SuperH Ltd.
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define SHHI shlld
+#define SHLO shlrd
+#else
+#define SHHI shlrd
+#define SHLO shlld
+#endif
+
+       .section .text..SHmedia32,"ax"
+       .globl  strcpy
+       .type   strcpy, @function
+       .align 5
+
+strcpy:
+
+       pta/l shortstring,tr1
+       ldlo.q r3,0,r4
+       ptabs r18,tr4
+       shlli r3,3,r7
+       addi r2, 8, r0
+       mcmpeq.b r4,r63,r6
+       SHHI r6,r7,r6
+       bnei/u r6,0,tr1 // shortstring
+       pta/l no_lddst, tr2
+       ori r3,-8,r23
+       sub r2, r23, r0
+       sub r3, r2, r21
+       addi r21, 8, r20
+       ldx.q r0, r21, r5
+       pta/l loop, tr0
+       ori r2,-8,r22
+       mcmpeq.b r5, r63, r6
+       bgt/u r22, r23, tr2 // no_lddst
+
+       // r22 < r23 :  Need to do a load from the destination.
+       // r22 == r23 : Doesn't actually need to load from destination,
+       //              but still can be handled here.
+       ldlo.q r2, 0, r9
+       movi -1, r8
+       SHLO r8, r7, r8
+       mcmv r4, r8, r9
+       stlo.q r2, 0, r9
+       beqi/l r6, 0, tr0 // loop
+
+       add r5, r63, r4
+       addi r0, 8, r0
+       blink tr1, r63 // shortstring
+no_lddst:
+       // r22 > r23: note that for r22 == r23 the sthi.q would clobber
+       //            bytes before the destination region.
+       stlo.q r2, 0, r4
+       SHHI r4, r7, r4
+       sthi.q r0, -1, r4
+       beqi/l r6, 0, tr0 // loop
+
+       add r5, r63, r4
+       addi r0, 8, r0
+shortstring:
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+       pta/l shortstring2,tr1
+       byterev r4,r4
+#endif
+shortstring2:
+       st.b r0,-8,r4
+       andi r4,0xff,r5
+       shlri r4,8,r4
+       addi r0,1,r0
+       bnei/l r5,0,tr1
+       blink tr4,r63 // return
+       
+       .balign 8
+loop:
+       stlo.q r0, 0, r5
+       ldx.q r0, r20, r4
+       addi r0, 16, r0
+       sthi.q r0, -9, r5
+       mcmpeq.b r4, r63, r6
+       bnei/u r6, 0, tr1 // shortstring
+       ldx.q r0, r21, r5
+       stlo.q r0, -8, r4
+       sthi.q r0, -1, r4
+       mcmpeq.b r5, r63, r6
+       beqi/l r6, 0, tr0 // loop
+
+       add r5, r63, r4
+       addi r0, 8, r0
+       blink tr1, r63 // shortstring
+
+       .size   strcpy,.-strcpy
diff --git a/arch/sh/lib64/strlen.S b/arch/sh/lib64/strlen.S
new file mode 100644 (file)
index 0000000..cbc0d91
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Simplistic strlen() implementation for SHmedia.
+ *
+ * Copyright (C) 2003  Paul Mundt <lethal@linux-sh.org>
+ */
+
+       .section .text..SHmedia32,"ax"
+       .globl  strlen
+       .type   strlen,@function
+
+       .balign 16
+strlen:
+       ptabs   r18, tr4
+
+       /*
+        * Note: We could easily deal with the NULL case here with a simple
+        * sanity check, though it seems that the behavior we want is to fault
+        * in the event that r2 == NULL, so we don't bother.
+        */
+/*     beqi    r2, 0, tr4 */   ! Sanity check
+
+       movi    -1, r0
+       pta/l   loop, tr0
+loop:
+       ld.b    r2, 0, r1
+       addi    r2, 1, r2
+       addi    r0, 1, r0
+       bnei/l  r1, 0, tr0
+
+       or      r0, r63, r2
+       blink   tr4, r63
+
+       .size   strlen,.-strlen
diff --git a/arch/sh/lib64/udivdi3.S b/arch/sh/lib64/udivdi3.S
new file mode 100644 (file)
index 0000000..6895c02
--- /dev/null
@@ -0,0 +1,120 @@
+       .section        .text..SHmedia32,"ax"
+       .align  2
+       .global __udivdi3
+__udivdi3:
+       shlri r3,1,r4
+       nsb r4,r22
+       shlld r3,r22,r6
+       shlri r6,49,r5
+       movi 0xffffffffffffbaf1,r21 /* .l shift count 17.  */
+       sub r21,r5,r1
+       mmulfx.w r1,r1,r4
+       mshflo.w r1,r63,r1
+       sub r63,r22,r20 // r63 == 64 % 64
+       mmulfx.w r5,r4,r4
+       pta large_divisor,tr0
+       addi r20,32,r9
+       msub.w r1,r4,r1
+       madd.w r1,r1,r1
+       mmulfx.w r1,r1,r4
+       shlri r6,32,r7
+       bgt/u r9,r63,tr0 // large_divisor
+       mmulfx.w r5,r4,r4
+       shlri r2,32+14,r19
+       addi r22,-31,r0
+       msub.w r1,r4,r1
+
+       mulu.l r1,r7,r4
+       addi r1,-3,r5
+       mulu.l r5,r19,r5
+       sub r63,r4,r4 // Negate to make sure r1 ends up <= 1/r2
+       shlri r4,2,r4 /* chop off leading %0000000000000000 001.00000000000 - or, as
+                        the case may be, %0000000000000000 000.11111111111, still */
+       muls.l r1,r4,r4 /* leaving at least one sign bit.  */
+       mulu.l r5,r3,r8
+       mshalds.l r1,r21,r1
+       shari r4,26,r4
+       shlld r8,r0,r8
+       add r1,r4,r1 // 31 bit unsigned reciprocal now in r1 (msb equiv. 0.5)
+       sub r2,r8,r2
+       /* Can do second step of 64 : 32 div now, using r1 and the rest in r2.  */
+
+       shlri r2,22,r21
+       mulu.l r21,r1,r21
+       shlld r5,r0,r8
+       addi r20,30-22,r0
+       shlrd r21,r0,r21
+       mulu.l r21,r3,r5
+       add r8,r21,r8
+       mcmpgt.l r21,r63,r21 // See Note 1
+       addi r20,30,r0
+       mshfhi.l r63,r21,r21
+       sub r2,r5,r2
+       andc r2,r21,r2
+
+       /* small divisor: need a third divide step */
+       mulu.l r2,r1,r7
+       ptabs r18,tr0
+       addi r2,1,r2
+       shlrd r7,r0,r7
+       mulu.l r7,r3,r5
+       add r8,r7,r8
+       sub r2,r3,r2
+       cmpgt r2,r5,r5
+       add r8,r5,r2
+       /* could test r3 here to check for divide by zero.  */
+       blink tr0,r63
+
+large_divisor:
+       mmulfx.w r5,r4,r4
+       shlrd r2,r9,r25
+       shlri r25,32,r8
+       msub.w r1,r4,r1
+
+       mulu.l r1,r7,r4
+       addi r1,-3,r5
+       mulu.l r5,r8,r5
+       sub r63,r4,r4 // Negate to make sure r1 ends up <= 1/r2
+       shlri r4,2,r4 /* chop off leading %0000000000000000 001.00000000000 - or, as
+                        the case may be, %0000000000000000 000.11111111111, still */
+       muls.l r1,r4,r4 /* leaving at least one sign bit.  */
+       shlri r5,14-1,r8
+       mulu.l r8,r7,r5
+       mshalds.l r1,r21,r1
+       shari r4,26,r4
+       add r1,r4,r1 // 31 bit unsigned reciprocal now in r1 (msb equiv. 0.5)
+       sub r25,r5,r25
+       /* Can do second step of 64 : 32 div now, using r1 and the rest in r25.  */
+
+       shlri r25,22,r21
+       mulu.l r21,r1,r21
+       pta no_lo_adj,tr0
+       addi r22,32,r0
+       shlri r21,40,r21
+       mulu.l r21,r7,r5
+       add r8,r21,r8
+       shlld r2,r0,r2
+       sub r25,r5,r25
+       bgtu/u r7,r25,tr0 // no_lo_adj
+       addi r8,1,r8
+       sub r25,r7,r25
+no_lo_adj:
+       mextr4 r2,r25,r2
+
+       /* large_divisor: only needs a few adjustments.  */
+       mulu.l r8,r6,r5
+       ptabs r18,tr0
+       /* bubble */
+       cmpgtu r5,r2,r5
+       sub r8,r5,r2
+       blink tr0,r63
+       
+/* Note 1: To shift the result of the second divide stage so that the result
+   always fits into 32 bits, yet we still reduce the rest sufficiently
+   would require a lot of instructions to do the shifts just right.  Using
+   the full 64 bit shift result to multiply with the divisor would require
+   four extra instructions for the upper 32 bits (shift / mulu / shift / sub).
+   Fortunately, if the upper 32 bits of the shift result are nonzero, we
+   know that the rest after taking this partial result into account will
+   fit into 32 bits.  So we just clear the upper 32 bits of the rest if the
+   upper 32 bits of the partial result are nonzero.  */
diff --git a/arch/sh/lib64/udivsi3.S b/arch/sh/lib64/udivsi3.S
new file mode 100644 (file)
index 0000000..e68120e
--- /dev/null
@@ -0,0 +1,59 @@
+       .global __udivsi3
+       .section        .text..SHmedia32,"ax"
+       .align  2
+
+/*
+   inputs: r4,r5
+   clobbered: r18,r19,r20,r21,r22,r25,tr0
+   result in r0.
+ */
+__udivsi3:
+       addz.l r5,r63,r22
+       nsb r22,r0
+       shlld r22,r0,r25
+       shlri r25,48,r25
+       movi 0xffffffffffffbb0c,r20 /* shift count eqiv 76 */
+       sub r20,r25,r21
+       mmulfx.w r21,r21,r19
+       mshflo.w r21,r63,r21
+       ptabs r18,tr0
+       mmulfx.w r25,r19,r19
+       sub r20,r0,r0
+       /* bubble */
+       msub.w r21,r19,r19
+
+       /*
+        * It would be nice for scheduling to do this add to r21 before
+        * the msub.w, but we need a different value for r19 to keep
+        * errors under control.
+        */
+       addi r19,-2,r21
+       mulu.l r4,r21,r18
+       mmulfx.w r19,r19,r19
+       shlli r21,15,r21
+       shlrd r18,r0,r18
+       mulu.l r18,r22,r20
+       mmacnfx.wl r25,r19,r21
+       /* bubble */
+       sub r4,r20,r25
+
+       mulu.l r25,r21,r19
+       addi r0,14,r0
+       /* bubble */
+       shlrd r19,r0,r19
+       mulu.l r19,r22,r20
+       add r18,r19,r18
+       /* bubble */
+       sub.l r25,r20,r25
+
+       mulu.l r25,r21,r19
+       addz.l r25,r63,r25
+       sub r25,r22,r25
+       shlrd r19,r0,r19
+       mulu.l r19,r22,r20
+       addi r25,1,r25
+       add r18,r19,r18
+
+       cmpgt r25,r20,r25
+       add.l r18,r25,r0
+       blink tr0,r63
index f066e76..cb2f3f2 100644 (file)
@@ -18,6 +18,7 @@ mmu-y                 := tlb-nommu.o pg-nommu.o
 mmu-$(CONFIG_MMU)      := fault_32.o tlbflush_32.o ioremap_32.o
 
 obj-y                  += $(mmu-y)
+obj-$(CONFIG_DEBUG_FS) += asids-debugfs.o
 
 ifdef CONFIG_DEBUG_FS
 obj-$(CONFIG_CPU_SH4)  += cache-debugfs.o
index 9481d0f..2863ffb 100644 (file)
@@ -13,6 +13,7 @@ obj-y                 += cache-sh5.o
 endif
 
 obj-y                  += $(mmu-y)
+obj-$(CONFIG_DEBUG_FS) += asids-debugfs.o
 
 obj-$(CONFIG_HUGETLB_PAGE)     += hugetlbpage.o
 obj-$(CONFIG_NUMA)             += numa.o
diff --git a/arch/sh/mm/asids-debugfs.c b/arch/sh/mm/asids-debugfs.c
new file mode 100644 (file)
index 0000000..8e912a1
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * debugfs ops for process ASIDs
+ *
+ *  Copyright (C) 2000, 2001  Paolo Alberelli
+ *  Copyright (C) 2003 - 2008  Paul Mundt
+ *  Copyright (C) 2003, 2004  Richard Curnow
+ *
+ * Provides a debugfs file that lists out the ASIDs currently associated
+ * with the processes.
+ *
+ * In the SH-5 case, if the DM.PC register is examined through the debug
+ * link, this shows ASID + PC. To make use of this, the PID->ASID
+ * relationship needs to be known. This is primarily for debugging.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/spinlock.h>
+#include <asm/processor.h>
+#include <asm/mmu_context.h>
+
+static int asids_seq_show(struct seq_file *file, void *iter)
+{
+       struct task_struct *p;
+
+       read_lock(&tasklist_lock);
+
+       for_each_process(p) {
+               int pid = p->pid;
+
+               if (unlikely(!pid))
+                       continue;
+
+               if (p->mm)
+                       seq_printf(file, "%5d : %02lx\n", pid,
+                                  cpu_asid(smp_processor_id(), p->mm));
+               else
+                       seq_printf(file, "%5d : (none)\n", pid);
+       }
+
+       read_unlock(&tasklist_lock);
+
+       return 0;
+}
+
+static int asids_debugfs_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, asids_seq_show, inode->i_private);
+}
+
+static const struct file_operations asids_debugfs_fops = {
+       .owner          = THIS_MODULE,
+       .open           = asids_debugfs_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int __init asids_debugfs_init(void)
+{
+       struct dentry *asids_dentry;
+
+       asids_dentry = debugfs_create_file("asids", S_IRUSR, sh_debugfs_root,
+                                          NULL, &asids_debugfs_fops);
+       if (!asids_dentry)
+               return -ENOMEM;
+       if (IS_ERR(asids_dentry))
+               return PTR_ERR(asids_dentry);
+
+       return 0;
+}
+module_init(asids_debugfs_init);
+
+MODULE_LICENSE("GPL v2");
index 9f8ea3a..edcd5fb 100644 (file)
@@ -42,6 +42,8 @@ void *dma_alloc_coherent(struct device *dev, size_t size,
                return NULL;
        }
 
+       split_page(pfn_to_page(virt_to_phys(ret) >> PAGE_SHIFT), order);
+
        *dma_handle = virt_to_phys(ret);
        return ret_nocache;
 }
@@ -51,10 +53,13 @@ void dma_free_coherent(struct device *dev, size_t size,
                         void *vaddr, dma_addr_t dma_handle)
 {
        int order = get_order(size);
+       unsigned long pfn = dma_handle >> PAGE_SHIFT;
+       int k;
 
        if (!dma_release_from_coherent(dev, order, vaddr)) {
                WARN_ON(irqs_disabled());       /* for portability */
-               free_pages((unsigned long)phys_to_virt(dma_handle), order);
+               for (k = 0; k < (1 << order); k++)
+                       __free_pages(pfn_to_page(pfn + k), 0);
                iounmap(vaddr);
        }
 }
index 898d477..31a33eb 100644 (file)
@@ -20,7 +20,6 @@
 #include <asm/system.h>
 #include <asm/mmu_context.h>
 #include <asm/tlbflush.h>
-#include <asm/kgdb.h>
 
 /*
  * This routine handles page faults.  It determines the address,
@@ -265,17 +264,6 @@ static inline int notify_page_fault(struct pt_regs *regs, int trap)
        return ret;
 }
 
-#ifdef CONFIG_SH_STORE_QUEUES
-/*
- * This is a special case for the SH-4 store queues, as pages for this
- * space still need to be faulted in before it's possible to flush the
- * store queue cache for writeout to the remapped region.
- */
-#define P3_ADDR_MAX            (P4SEG_STORE_QUE + 0x04000000)
-#else
-#define P3_ADDR_MAX            P4SEG
-#endif
-
 /*
  * Called with interrupts disabled.
  */
@@ -293,11 +281,6 @@ asmlinkage int __kprobes __do_page_fault(struct pt_regs *regs,
        if (notify_page_fault(regs, lookup_exception_vector()))
                goto out;
 
-#ifdef CONFIG_SH_KGDB
-       if (kgdb_nofault && kgdb_bus_err_hook)
-               kgdb_bus_err_hook();
-#endif
-
        ret = 1;
 
        /*
index 882a32e..32946fb 100644 (file)
@@ -116,9 +116,10 @@ EXPORT_SYMBOL(__ioremap);
 void __iounmap(void __iomem *addr)
 {
        unsigned long vaddr = (unsigned long __force)addr;
+       unsigned long seg = PXSEG(vaddr);
        struct vm_struct *p;
 
-       if (PXSEG(vaddr) < P3SEG || is_pci_memaddr(vaddr))
+       if (seg < P3SEG || seg >= P3_ADDR_MAX || is_pci_memaddr(vaddr))
                return;
 
 #ifdef CONFIG_32BIT
index 8837d51..931f4d0 100644 (file)
@@ -9,7 +9,101 @@
  */
 #include <linux/io.h>
 #include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/module.h>
 #include <asm/page.h>
+#include <asm/processor.h>
+
+#ifdef CONFIG_MMU
+unsigned long shm_align_mask = PAGE_SIZE - 1;  /* Sane caches */
+EXPORT_SYMBOL(shm_align_mask);
+
+/*
+ * To avoid cache aliases, we map the shared page with same color.
+ */
+#define COLOUR_ALIGN(addr, pgoff)                              \
+       ((((addr) + shm_align_mask) & ~shm_align_mask) +        \
+        (((pgoff) << PAGE_SHIFT) & shm_align_mask))
+
+unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
+       unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma;
+       unsigned long start_addr;
+       int do_colour_align;
+
+       if (flags & MAP_FIXED) {
+               /* We do not accept a shared mapping if it would violate
+                * cache aliasing constraints.
+                */
+               if ((flags & MAP_SHARED) && (addr & shm_align_mask))
+                       return -EINVAL;
+               return addr;
+       }
+
+       if (unlikely(len > TASK_SIZE))
+               return -ENOMEM;
+
+       do_colour_align = 0;
+       if (filp || (flags & MAP_SHARED))
+               do_colour_align = 1;
+
+       if (addr) {
+               if (do_colour_align)
+                       addr = COLOUR_ALIGN(addr, pgoff);
+               else
+                       addr = PAGE_ALIGN(addr);
+
+               vma = find_vma(mm, addr);
+               if (TASK_SIZE - len >= addr &&
+                   (!vma || addr + len <= vma->vm_start))
+                       return addr;
+       }
+
+       if (len > mm->cached_hole_size) {
+               start_addr = addr = mm->free_area_cache;
+       } else {
+               mm->cached_hole_size = 0;
+               start_addr = addr = TASK_UNMAPPED_BASE;
+       }
+
+full_search:
+       if (do_colour_align)
+               addr = COLOUR_ALIGN(addr, pgoff);
+       else
+               addr = PAGE_ALIGN(mm->free_area_cache);
+
+       for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
+               /* At this point:  (!vma || addr < vma->vm_end). */
+               if (unlikely(TASK_SIZE - len < addr)) {
+                       /*
+                        * Start a new search - just in case we missed
+                        * some holes.
+                        */
+                       if (start_addr != TASK_UNMAPPED_BASE) {
+                               start_addr = addr = TASK_UNMAPPED_BASE;
+                               mm->cached_hole_size = 0;
+                               goto full_search;
+                       }
+                       return -ENOMEM;
+               }
+               if (likely(!vma || addr + len <= vma->vm_start)) {
+                       /*
+                        * Remember the place where we stopped the search:
+                        */
+                       mm->free_area_cache = addr + len;
+                       return addr;
+               }
+               if (addr + mm->cached_hole_size < vma->vm_start)
+                       mm->cached_hole_size = vma->vm_start - addr;
+
+               addr = vma->vm_end;
+               if (do_colour_align)
+                       addr = COLOUR_ALIGN(addr, pgoff);
+       }
+}
+#endif /* CONFIG_MMU */
 
 /*
  * You really shouldn't be using read() or write() on /dev/mem.  This
index 2efc2e7..8e6eec9 100644 (file)
@@ -6,13 +6,8 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
                oprofilefs.o oprofile_stats.o \
                timer_int.o )
 
-profdrvr-y                             := op_model_null.o
+oprofile-y     := $(DRIVER_OBJS) common.o backtrace.o
 
-# SH7750-style performance counters exist across 7750/7750S and 7091.
-profdrvr-$(CONFIG_CPU_SUBTYPE_SH7750S) := op_model_sh7750.o
-profdrvr-$(CONFIG_CPU_SUBTYPE_SH7750)  := op_model_sh7750.o
-profdrvr-$(CONFIG_CPU_SUBTYPE_SH7091)  := op_model_sh7750.o
-
-oprofile-y                             := $(DRIVER_OBJS) $(profdrvr-y)
-
-EXTRA_CFLAGS += -Werror
+oprofile-$(CONFIG_CPU_SUBTYPE_SH7750S) += op_model_sh7750.o
+oprofile-$(CONFIG_CPU_SUBTYPE_SH7750)  += op_model_sh7750.o
+oprofile-$(CONFIG_CPU_SUBTYPE_SH7091)  += op_model_sh7750.o
diff --git a/arch/sh/oprofile/backtrace.c b/arch/sh/oprofile/backtrace.c
new file mode 100644 (file)
index 0000000..9499a29
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * SH specific backtracing code for oprofile
+ *
+ * Copyright 2007 STMicroelectronics Ltd.
+ *
+ * Author: Dave Peverley <dpeverley@mpc-data.co.uk>
+ *
+ * Based on ARM oprofile backtrace code by Richard Purdie and in turn, i386
+ * oprofile backtrace code by John Levon, David Smith
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/oprofile.h>
+#include <linux/sched.h>
+#include <linux/kallsyms.h>
+#include <linux/mm.h>
+#include <asm/ptrace.h>
+#include <asm/uaccess.h>
+#include <asm/sections.h>
+
+/* Limit to stop backtracing too far. */
+static int backtrace_limit = 20;
+
+static unsigned long *
+user_backtrace(unsigned long *stackaddr, struct pt_regs *regs)
+{
+       unsigned long buf_stack;
+
+       /* Also check accessibility of address */
+       if (!access_ok(VERIFY_READ, stackaddr, sizeof(unsigned long)))
+               return NULL;
+
+       if (__copy_from_user_inatomic(&buf_stack, stackaddr, sizeof(unsigned long)))
+               return NULL;
+
+       /* Quick paranoia check */
+       if (buf_stack & 3)
+               return NULL;
+
+       oprofile_add_trace(buf_stack);
+
+       stackaddr++;
+
+       return stackaddr;
+}
+
+/*
+ * |             | /\ Higher addresses
+ * |             |
+ * --------------- stack base (address of current_thread_info)
+ * | thread info |
+ * .             .
+ * |    stack    |
+ * --------------- saved regs->regs[15] value if valid
+ * .             .
+ * --------------- struct pt_regs stored on stack (struct pt_regs *)
+ * |             |
+ * .             .
+ * |             |
+ * --------------- ???
+ * |             |
+ * |             | \/ Lower addresses
+ *
+ * Thus, &pt_regs <-> stack base restricts the valid(ish) fp values
+ */
+static int valid_kernel_stack(unsigned long *stackaddr, struct pt_regs *regs)
+{
+       unsigned long stack = (unsigned long)regs;
+       unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE;
+
+       return ((unsigned long)stackaddr > stack) && ((unsigned long)stackaddr < stack_base);
+}
+
+static unsigned long *
+kernel_backtrace(unsigned long *stackaddr, struct pt_regs *regs)
+{
+       unsigned long addr;
+
+       /*
+        * If not a valid kernel address, keep going till we find one
+        * or the SP stops being a valid address.
+        */
+       do {
+               addr = *stackaddr++;
+               oprofile_add_trace(addr);
+       } while (valid_kernel_stack(stackaddr, regs));
+
+       return stackaddr;
+}
+
+void sh_backtrace(struct pt_regs * const regs, unsigned int depth)
+{
+       unsigned long *stackaddr;
+
+       /*
+        * Paranoia - clip max depth as we could get lost in the weeds.
+        */
+       if (depth > backtrace_limit)
+               depth = backtrace_limit;
+
+       stackaddr = (unsigned long *)regs->regs[15];
+       if (!user_mode(regs)) {
+               while (depth-- && valid_kernel_stack(stackaddr, regs))
+                       stackaddr = kernel_backtrace(stackaddr, regs);
+
+               return;
+       }
+
+       while (depth-- && (stackaddr != NULL))
+               stackaddr = user_backtrace(stackaddr, regs);
+}
diff --git a/arch/sh/oprofile/common.c b/arch/sh/oprofile/common.c
new file mode 100644 (file)
index 0000000..1d97d64
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * arch/sh/oprofile/init.c
+ *
+ * Copyright (C) 2003 - 2008  Paul Mundt
+ *
+ * Based on arch/mips/oprofile/common.c:
+ *
+ *     Copyright (C) 2004, 2005 Ralf Baechle
+ *     Copyright (C) 2005 MIPS Technologies, Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/oprofile.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+#include <asm/processor.h>
+#include "op_impl.h"
+
+extern struct op_sh_model op_model_sh7750_ops __weak;
+extern struct op_sh_model op_model_sh4a_ops __weak;
+
+static struct op_sh_model *model;
+
+static struct op_counter_config ctr[20];
+
+extern void sh_backtrace(struct pt_regs * const regs, unsigned int depth);
+
+static int op_sh_setup(void)
+{
+       /* Pre-compute the values to stuff in the hardware registers.  */
+       model->reg_setup(ctr);
+
+       /* Configure the registers on all cpus.  */
+       on_each_cpu(model->cpu_setup, NULL, 1);
+
+        return 0;
+}
+
+static int op_sh_create_files(struct super_block *sb, struct dentry *root)
+{
+       int i, ret = 0;
+
+       for (i = 0; i < model->num_counters; i++) {
+               struct dentry *dir;
+               char buf[4];
+
+               snprintf(buf, sizeof(buf), "%d", i);
+               dir = oprofilefs_mkdir(sb, root, buf);
+
+               ret |= oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
+               ret |= oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
+               ret |= oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
+               ret |= oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
+
+               if (model->create_files)
+                       ret |= model->create_files(sb, dir);
+               else
+                       ret |= oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count);
+
+               /* Dummy entries */
+               ret |= oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
+       }
+
+       return ret;
+}
+
+static int op_sh_start(void)
+{
+       /* Enable performance monitoring for all counters.  */
+       on_each_cpu(model->cpu_start, NULL, 1);
+
+       return 0;
+}
+
+static void op_sh_stop(void)
+{
+       /* Disable performance monitoring for all counters.  */
+       on_each_cpu(model->cpu_stop, NULL, 1);
+}
+
+int __init oprofile_arch_init(struct oprofile_operations *ops)
+{
+       struct op_sh_model *lmodel = NULL;
+       int ret;
+
+       /*
+        * Always assign the backtrace op. If the counter initialization
+        * fails, we fall back to the timer which will still make use of
+        * this.
+        */
+       ops->backtrace = sh_backtrace;
+
+       switch (current_cpu_data.type) {
+       /* SH-4 types */
+       case CPU_SH7750:
+       case CPU_SH7750S:
+               lmodel = &op_model_sh7750_ops;
+               break;
+
+        /* SH-4A types */
+       case CPU_SH7763:
+       case CPU_SH7770:
+       case CPU_SH7780:
+       case CPU_SH7781:
+       case CPU_SH7785:
+       case CPU_SH7723:
+       case CPU_SHX3:
+               lmodel = &op_model_sh4a_ops;
+               break;
+
+       /* SH4AL-DSP types */
+       case CPU_SH7343:
+       case CPU_SH7722:
+       case CPU_SH7366:
+               lmodel = &op_model_sh4a_ops;
+               break;
+       }
+
+       if (!lmodel)
+               return -ENODEV;
+       if (!(current_cpu_data.flags & CPU_HAS_PERF_COUNTER))
+               return -ENODEV;
+
+       ret = lmodel->init();
+       if (unlikely(ret != 0))
+               return ret;
+
+       model = lmodel;
+
+       ops->setup              = op_sh_setup;
+       ops->create_files       = op_sh_create_files;
+       ops->start              = op_sh_start;
+       ops->stop               = op_sh_stop;
+       ops->cpu_type           = lmodel->cpu_type;
+
+       printk(KERN_INFO "oprofile: using %s performance monitoring.\n",
+              lmodel->cpu_type);
+
+       return 0;
+}
+
+void oprofile_arch_exit(void)
+{
+       if (model && model->exit)
+               model->exit();
+}
diff --git a/arch/sh/oprofile/op_impl.h b/arch/sh/oprofile/op_impl.h
new file mode 100644 (file)
index 0000000..4d50997
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef __OP_IMPL_H
+#define __OP_IMPL_H
+
+/* Per-counter configuration as set via oprofilefs.  */
+struct op_counter_config {
+       unsigned long enabled;
+       unsigned long event;
+
+       unsigned long long count;
+
+       /* Dummy values for userspace tool compliance */
+       unsigned long kernel;
+       unsigned long user;
+       unsigned long unit_mask;
+};
+
+/* Per-architecture configury and hooks.  */
+struct op_sh_model {
+       void (*reg_setup)(struct op_counter_config *);
+       int (*create_files)(struct super_block *sb, struct dentry *dir);
+       void (*cpu_setup)(void *dummy);
+       int (*init)(void);
+       void (*exit)(void);
+       void (*cpu_start)(void *args);
+       void (*cpu_stop)(void *args);
+       char *cpu_type;
+       unsigned char num_counters;
+};
+
+/* arch/sh/oprofile/common.c */
+extern void sh_backtrace(struct pt_regs * const regs, unsigned int depth);
+
+#endif /* __OP_IMPL_H */
diff --git a/arch/sh/oprofile/op_model_null.c b/arch/sh/oprofile/op_model_null.c
deleted file mode 100644 (file)
index a845b08..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * arch/sh/oprofile/op_model_null.c
- *
- * Copyright (C) 2003  Paul Mundt
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#include <linux/kernel.h>
-#include <linux/oprofile.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-
-int __init oprofile_arch_init(struct oprofile_operations *ops)
-{
-       return -ENODEV;
-}
-
-void oprofile_arch_exit(void)
-{
-}
-
index 008b3b0..c892c7c 100644 (file)
@@ -3,7 +3,7 @@
  *
  * OProfile support for SH7750/SH7750S Performance Counters
  *
- * Copyright (C) 2003, 2004  Paul Mundt
+ * Copyright (C) 2003 - 2008  Paul Mundt
  *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file "COPYING" in the main directory of this archive
 #include <linux/init.h>
 #include <linux/errno.h>
 #include <linux/interrupt.h>
+#include <linux/io.h>
 #include <linux/fs.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
+#include "op_impl.h"
 
 #define PM_CR_BASE     0xff000084      /* 16-bit */
 #define PM_CTR_BASE    0xff100004      /* 32-bit */
 
-#define PMCR1          (PM_CR_BASE  + 0x00)
-#define PMCR2          (PM_CR_BASE  + 0x04)
-#define PMCTR1H                (PM_CTR_BASE + 0x00)
-#define PMCTR1L                (PM_CTR_BASE + 0x04)
-#define PMCTR2H                (PM_CTR_BASE + 0x08)
-#define PMCTR2L                (PM_CTR_BASE + 0x0c)
+#define PMCR(n)                (PM_CR_BASE + ((n) * 0x04))
+#define PMCTRH(n)      (PM_CTR_BASE + 0x00 + ((n) * 0x08))
+#define PMCTRL(n)      (PM_CTR_BASE + 0x04 + ((n) * 0x08))
 
 #define PMCR_PMM_MASK  0x0000003f
 
 #define PMCR_PMST      0x00004000
 #define PMCR_PMEN      0x00008000
 
-#define PMCR_ENABLE    (PMCR_PMST | PMCR_PMEN)
+struct op_sh_model op_model_sh7750_ops;
 
-/*
- * SH7750/SH7750S have 2 perf counters
- */
 #define NR_CNTRS       2
 
-struct op_counter_config {
-       unsigned long enabled;
-       unsigned long event;
-       unsigned long count;
-
-       /* Dummy values for userspace tool compliance */
-       unsigned long kernel;
-       unsigned long user;
-       unsigned long unit_mask;
-};
-
-static struct op_counter_config ctr[NR_CNTRS];
+static struct sh7750_ppc_register_config {
+       unsigned int ctrl;
+       unsigned long cnt_hi;
+       unsigned long cnt_lo;
+} regcache[NR_CNTRS];
 
 /*
  * There are a number of events supported by each counter (33 in total).
@@ -116,12 +103,8 @@ static int sh7750_timer_notify(struct pt_regs *regs)
 
 static u64 sh7750_read_counter(int counter)
 {
-       u32 hi, lo;
-
-       hi = (counter == 0) ? ctrl_inl(PMCTR1H) : ctrl_inl(PMCTR2H);
-       lo = (counter == 0) ? ctrl_inl(PMCTR1L) : ctrl_inl(PMCTR2L);
-
-       return (u64)((u64)(hi & 0xffff) << 32) | lo;
+       return (u64)((u64)(__raw_readl(PMCTRH(counter)) & 0xffff) << 32) |
+                          __raw_readl(PMCTRL(counter));
 }
 
 /*
@@ -170,11 +153,7 @@ static ssize_t sh7750_write_count(struct file *file, const char __user *buf,
         */
        WARN_ON(val != 0);
 
-       if (counter == 0) {
-               ctrl_outw(ctrl_inw(PMCR1) | PMCR_PMCLR, PMCR1);
-       } else {
-               ctrl_outw(ctrl_inw(PMCR2) | PMCR_PMCLR, PMCR2);
-       }
+       __raw_writew(__raw_readw(PMCR(counter)) | PMCR_PMCLR, PMCR(counter));
 
        return count;
 }
@@ -184,88 +163,93 @@ static const struct file_operations count_fops = {
        .write          = sh7750_write_count,
 };
 
-static int sh7750_perf_counter_create_files(struct super_block *sb, struct dentry *root)
+static int sh7750_ppc_create_files(struct super_block *sb, struct dentry *dir)
 {
-       int i;
+       return oprofilefs_create_file(sb, dir, "count", &count_fops);
+}
 
-       for (i = 0; i < NR_CNTRS; i++) {
-               struct dentry *dir;
-               char buf[4];
+static void sh7750_ppc_reg_setup(struct op_counter_config *ctr)
+{
+       unsigned int counters = op_model_sh7750_ops.num_counters;
+       int i;
 
-               snprintf(buf, sizeof(buf), "%d", i);
-               dir = oprofilefs_mkdir(sb, root, buf);
+       for (i = 0; i < counters; i++) {
+               regcache[i].ctrl        = 0;
+               regcache[i].cnt_hi      = 0;
+               regcache[i].cnt_lo      = 0;
 
-               oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
-               oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
-               oprofilefs_create_file(sb, dir, "count", &count_fops);
+               if (!ctr[i].enabled)
+                       continue;
 
-               /* Dummy entries */
-               oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
-               oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
-               oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
+               regcache[i].ctrl |= ctr[i].event | PMCR_PMEN | PMCR_PMST;
+               regcache[i].cnt_hi = (unsigned long)((ctr->count >> 32) & 0xffff);
+               regcache[i].cnt_lo = (unsigned long)(ctr->count & 0xffffffff);
        }
-
-       return 0;
 }
 
-static int sh7750_perf_counter_start(void)
+static void sh7750_ppc_cpu_setup(void *args)
 {
-       u16 pmcr;
-
-       /* Enable counter 1 */
-       if (ctr[0].enabled) {
-               pmcr = ctrl_inw(PMCR1);
-               WARN_ON(pmcr & PMCR_PMEN);
-
-               pmcr &= ~PMCR_PMM_MASK;
-               pmcr |= ctr[0].event;
-               ctrl_outw(pmcr | PMCR_ENABLE, PMCR1);
-       }
-
-       /* Enable counter 2 */
-       if (ctr[1].enabled) {
-               pmcr = ctrl_inw(PMCR2);
-               WARN_ON(pmcr & PMCR_PMEN);
+       unsigned int counters = op_model_sh7750_ops.num_counters;
+       int i;
 
-               pmcr &= ~PMCR_PMM_MASK;
-               pmcr |= ctr[1].event;
-               ctrl_outw(pmcr | PMCR_ENABLE, PMCR2);
+       for (i = 0; i < counters; i++) {
+               __raw_writew(0, PMCR(i));
+               __raw_writel(regcache[i].cnt_hi, PMCTRH(i));
+               __raw_writel(regcache[i].cnt_lo, PMCTRL(i));
        }
-
-       return register_timer_hook(sh7750_timer_notify);
 }
 
-static void sh7750_perf_counter_stop(void)
+static void sh7750_ppc_cpu_start(void *args)
 {
-       ctrl_outw(ctrl_inw(PMCR1) & ~PMCR_PMEN, PMCR1);
-       ctrl_outw(ctrl_inw(PMCR2) & ~PMCR_PMEN, PMCR2);
+       unsigned int counters = op_model_sh7750_ops.num_counters;
+       int i;
 
-       unregister_timer_hook(sh7750_timer_notify);
+       for (i = 0; i < counters; i++)
+               __raw_writew(regcache[i].ctrl, PMCR(i));
 }
 
-static struct oprofile_operations sh7750_perf_counter_ops = {
-       .create_files   = sh7750_perf_counter_create_files,
-       .start          = sh7750_perf_counter_start,
-       .stop           = sh7750_perf_counter_stop,
-};
-
-int __init oprofile_arch_init(struct oprofile_operations *ops)
+static void sh7750_ppc_cpu_stop(void *args)
 {
-       if (!(current_cpu_data.flags & CPU_HAS_PERF_COUNTER))
-               return -ENODEV;
+       unsigned int counters = op_model_sh7750_ops.num_counters;
+       int i;
 
-       ops = &sh7750_perf_counter_ops;
-       ops->cpu_type = "sh/sh7750";
+       /* Disable the counters */
+       for (i = 0; i < counters; i++)
+               __raw_writew(__raw_readw(PMCR(i)) & ~PMCR_PMEN, PMCR(i));
+}
 
-       printk(KERN_INFO "oprofile: using SH-4 performance monitoring.\n");
+static inline void sh7750_ppc_reset(void)
+{
+       unsigned int counters = op_model_sh7750_ops.num_counters;
+       int i;
 
        /* Clear the counters */
-       ctrl_outw(ctrl_inw(PMCR1) | PMCR_PMCLR, PMCR1);
-       ctrl_outw(ctrl_inw(PMCR2) | PMCR_PMCLR, PMCR2);
+       for (i = 0; i < counters; i++)
+               __raw_writew(__raw_readw(PMCR(i)) | PMCR_PMCLR, PMCR(i));
+}
 
-       return 0;
+static int sh7750_ppc_init(void)
+{
+       sh7750_ppc_reset();
+
+       return register_timer_hook(sh7750_timer_notify);
 }
 
-void oprofile_arch_exit(void)
+static void sh7750_ppc_exit(void)
 {
+       unregister_timer_hook(sh7750_timer_notify);
+
+       sh7750_ppc_reset();
 }
+
+struct op_sh_model op_model_sh7750_ops = {
+       .cpu_type       = "sh/sh7750",
+       .num_counters   = NR_CNTRS,
+       .reg_setup      = sh7750_ppc_reg_setup,
+       .cpu_setup      = sh7750_ppc_cpu_setup,
+       .cpu_start      = sh7750_ppc_cpu_start,
+       .cpu_stop       = sh7750_ppc_cpu_stop,
+       .init           = sh7750_ppc_init,
+       .exit           = sh7750_ppc_exit,
+       .create_files   = sh7750_ppc_create_files,
+};
index d0c2928..284b7e8 100644 (file)
@@ -8,6 +8,7 @@
 SE                     SH_SOLUTION_ENGINE
 HIGHLANDER             SH_HIGHLANDER
 RTS7751R2D             SH_RTS7751R2D
+RSK                    SH_RSK
 
 #
 # List of companion chips / MFDs.
@@ -46,6 +47,7 @@ R2D_1                 RTS7751R2D_1
 CAYMAN                 SH_CAYMAN
 SDK7780                        SH_SDK7780
 MIGOR                  SH_MIGOR
+RSK7201                        SH_RSK7201
 RSK7203                        SH_RSK7203
 AP325RXA               SH_AP325RXA
 SH7763RDP              SH_SH7763RDP
index 8f44ebb..e14629c 100644 (file)
@@ -161,7 +161,8 @@ void mconsole_proc(struct mc_request *req)
                goto out_kill;
        }
 
-       file = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY);
+       file = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY,
+                          current_cred());
        if (IS_ERR(file)) {
                mconsole_reply(req, "Failed to open file", 1, 0);
                goto out_kill;
index 070afc5..b9d0026 100644 (file)
@@ -6,13 +6,22 @@
  * Intel(R) 64 and IA-32 Architectures Software Developer's Manual
  * Volume 2A: Instruction Set Reference, A-M
  *
- * Copyright (c) 2008 Austin Zhang <austin_zhang@linux.intel.com>
- * Copyright (c) 2008 Kent Liu <kent.liu@intel.com>
+ * Copyright (C) 2008 Intel Corporation
+ * Authors: Austin Zhang <austin_zhang@linux.intel.com>
+ *          Kent Liu <kent.liu@intel.com>
  *
  * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  *
  */
 #include <linux/init.h>
@@ -75,99 +84,92 @@ static u32 __pure crc32c_intel_le_hw(u32 crc, unsigned char const *p, size_t len
  * If your algorithm starts with ~0, then XOR with ~0 before you set
  * the seed.
  */
-static int crc32c_intel_setkey(struct crypto_ahash *hash, const u8 *key,
+static int crc32c_intel_setkey(struct crypto_shash *hash, const u8 *key,
                        unsigned int keylen)
 {
-       u32 *mctx = crypto_ahash_ctx(hash);
+       u32 *mctx = crypto_shash_ctx(hash);
 
        if (keylen != sizeof(u32)) {
-               crypto_ahash_set_flags(hash, CRYPTO_TFM_RES_BAD_KEY_LEN);
+               crypto_shash_set_flags(hash, CRYPTO_TFM_RES_BAD_KEY_LEN);
                return -EINVAL;
        }
        *mctx = le32_to_cpup((__le32 *)key);
        return 0;
 }
 
-static int crc32c_intel_init(struct ahash_request *req)
+static int crc32c_intel_init(struct shash_desc *desc)
 {
-       u32 *mctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
-       u32 *crcp = ahash_request_ctx(req);
+       u32 *mctx = crypto_shash_ctx(desc->tfm);
+       u32 *crcp = shash_desc_ctx(desc);
 
        *crcp = *mctx;
 
        return 0;
 }
 
-static int crc32c_intel_update(struct ahash_request *req)
+static int crc32c_intel_update(struct shash_desc *desc, const u8 *data,
+                              unsigned int len)
 {
-       struct crypto_hash_walk walk;
-       u32 *crcp = ahash_request_ctx(req);
-       u32 crc = *crcp;
-       int nbytes;
-
-       for (nbytes = crypto_hash_walk_first(req, &walk); nbytes;
-          nbytes = crypto_hash_walk_done(&walk, 0))
-       crc = crc32c_intel_le_hw(crc, walk.data, nbytes);
+       u32 *crcp = shash_desc_ctx(desc);
 
-       *crcp = crc;
+       *crcp = crc32c_intel_le_hw(*crcp, data, len);
        return 0;
 }
 
-static int crc32c_intel_final(struct ahash_request *req)
+static int __crc32c_intel_finup(u32 *crcp, const u8 *data, unsigned int len,
+                               u8 *out)
 {
-       u32 *crcp = ahash_request_ctx(req);
-
-       *(__le32 *)req->result = ~cpu_to_le32p(crcp);
+       *(__le32 *)out = ~cpu_to_le32(crc32c_intel_le_hw(*crcp, data, len));
        return 0;
 }
 
-static int crc32c_intel_digest(struct ahash_request *req)
+static int crc32c_intel_finup(struct shash_desc *desc, const u8 *data,
+                             unsigned int len, u8 *out)
 {
-       struct crypto_hash_walk walk;
-       u32 *mctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
-       u32 crc = *mctx;
-       int nbytes;
+       return __crc32c_intel_finup(shash_desc_ctx(desc), data, len, out);
+}
 
-       for (nbytes = crypto_hash_walk_first(req, &walk); nbytes;
-          nbytes = crypto_hash_walk_done(&walk, 0))
-               crc = crc32c_intel_le_hw(crc, walk.data, nbytes);
+static int crc32c_intel_final(struct shash_desc *desc, u8 *out)
+{
+       u32 *crcp = shash_desc_ctx(desc);
 
-       *(__le32 *)req->result = ~cpu_to_le32(crc);
+       *(__le32 *)out = ~cpu_to_le32p(crcp);
        return 0;
 }
 
+static int crc32c_intel_digest(struct shash_desc *desc, const u8 *data,
+                              unsigned int len, u8 *out)
+{
+       return __crc32c_intel_finup(crypto_shash_ctx(desc->tfm), data, len,
+                                   out);
+}
+
 static int crc32c_intel_cra_init(struct crypto_tfm *tfm)
 {
        u32 *key = crypto_tfm_ctx(tfm);
 
        *key = ~0;
 
-       tfm->crt_ahash.reqsize = sizeof(u32);
-
        return 0;
 }
 
-static struct crypto_alg alg = {
-       .cra_name               =       "crc32c",
-       .cra_driver_name        =       "crc32c-intel",
-       .cra_priority           =       200,
-       .cra_flags              =       CRYPTO_ALG_TYPE_AHASH,
-       .cra_blocksize          =       CHKSUM_BLOCK_SIZE,
-       .cra_alignmask          =       3,
-       .cra_ctxsize            =       sizeof(u32),
-       .cra_module             =       THIS_MODULE,
-       .cra_list               =       LIST_HEAD_INIT(alg.cra_list),
-       .cra_init               =       crc32c_intel_cra_init,
-       .cra_type               =       &crypto_ahash_type,
-       .cra_u                  =       {
-               .ahash = {
-                       .digestsize    =       CHKSUM_DIGEST_SIZE,
-                       .setkey        =       crc32c_intel_setkey,
-                       .init          =       crc32c_intel_init,
-                       .update        =       crc32c_intel_update,
-                       .final         =       crc32c_intel_final,
-                       .digest        =       crc32c_intel_digest,
-               }
+static struct shash_alg alg = {
+       .setkey                 =       crc32c_intel_setkey,
+       .init                   =       crc32c_intel_init,
+       .update                 =       crc32c_intel_update,
+       .final                  =       crc32c_intel_final,
+       .finup                  =       crc32c_intel_finup,
+       .digest                 =       crc32c_intel_digest,
+       .descsize               =       sizeof(u32),
+       .digestsize             =       CHKSUM_DIGEST_SIZE,
+       .base                   =       {
+               .cra_name               =       "crc32c",
+               .cra_driver_name        =       "crc32c-intel",
+               .cra_priority           =       200,
+               .cra_blocksize          =       CHKSUM_BLOCK_SIZE,
+               .cra_ctxsize            =       sizeof(u32),
+               .cra_module             =       THIS_MODULE,
+               .cra_init               =       crc32c_intel_cra_init,
        }
 };
 
@@ -175,14 +177,14 @@ static struct crypto_alg alg = {
 static int __init crc32c_intel_mod_init(void)
 {
        if (cpu_has_xmm4_2)
-               return crypto_register_alg(&alg);
+               return crypto_register_shash(&alg);
        else
                return -ENODEV;
 }
 
 static void __exit crc32c_intel_mod_fini(void)
 {
-       crypto_unregister_alg(&alg);
+       crypto_unregister_shash(&alg);
 }
 
 module_init(crc32c_intel_mod_init);
@@ -194,4 +196,3 @@ MODULE_LICENSE("GPL");
 
 MODULE_ALIAS("crc32c");
 MODULE_ALIAS("crc32c-intel");
-
index 127ec3f..2a4d073 100644 (file)
@@ -327,7 +327,7 @@ static int load_aout_binary(struct linux_binprm *bprm, struct pt_regs *regs)
        current->mm->cached_hole_size = 0;
 
        current->mm->mmap = NULL;
-       compute_creds(bprm);
+       install_exec_creds(bprm);
        current->flags &= ~PF_FORKNOEXEC;
 
        if (N_MAGIC(ex) == OMAGIC) {
index 20ef272..46b5f75 100644 (file)
@@ -393,7 +393,7 @@ static void show_fault_oops(struct pt_regs *regs, unsigned long error_code,
                if (pte && pte_present(*pte) && !pte_exec(*pte))
                        printk(KERN_CRIT "kernel tried to execute "
                                "NX-protected page - exploit attempt? "
-                               "(uid: %d)\n", current->uid);
+                               "(uid: %d)\n", current_uid());
        }
 #endif
 
index dc20a34..8dde4fc 100644 (file)
@@ -102,6 +102,7 @@ config CRYPTO_NULL
        tristate "Null algorithms"
        select CRYPTO_ALGAPI
        select CRYPTO_BLKCIPHER
+       select CRYPTO_HASH
        help
          These are 'Null' algorithms, used by IPsec, which do nothing.
 
@@ -256,12 +257,10 @@ comment "Digest"
 config CRYPTO_CRC32C
        tristate "CRC32c CRC algorithm"
        select CRYPTO_HASH
-       select LIBCRC32C
        help
          Castagnoli, et al Cyclic Redundancy-Check Algorithm.  Used
          by iSCSI for header and data digests and by others.
-         See Castagnoli93.  This implementation uses lib/libcrc32c.
-         Module will be crc32c.
+         See Castagnoli93.  Module will be crc32c.
 
 config CRYPTO_CRC32C_INTEL
        tristate "CRC32c INTEL hardware acceleration"
@@ -277,19 +276,19 @@ config CRYPTO_CRC32C_INTEL
 
 config CRYPTO_MD4
        tristate "MD4 digest algorithm"
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          MD4 message digest algorithm (RFC1320).
 
 config CRYPTO_MD5
        tristate "MD5 digest algorithm"
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          MD5 message digest algorithm (RFC1321).
 
 config CRYPTO_MICHAEL_MIC
        tristate "Michael MIC keyed digest algorithm"
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          Michael MIC is used for message integrity protection in TKIP
          (IEEE 802.11i). This algorithm is required for TKIP, but it
@@ -298,7 +297,7 @@ config CRYPTO_MICHAEL_MIC
 
 config CRYPTO_RMD128
        tristate "RIPEMD-128 digest algorithm"
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          RIPEMD-128 (ISO/IEC 10118-3:2004).
 
@@ -311,7 +310,7 @@ config CRYPTO_RMD128
 
 config CRYPTO_RMD160
        tristate "RIPEMD-160 digest algorithm"
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          RIPEMD-160 (ISO/IEC 10118-3:2004).
 
@@ -328,7 +327,7 @@ config CRYPTO_RMD160
 
 config CRYPTO_RMD256
        tristate "RIPEMD-256 digest algorithm"
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          RIPEMD-256 is an optional extension of RIPEMD-128 with a
          256 bit hash. It is intended for applications that require
@@ -340,7 +339,7 @@ config CRYPTO_RMD256
 
 config CRYPTO_RMD320
        tristate "RIPEMD-320 digest algorithm"
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          RIPEMD-320 is an optional extension of RIPEMD-160 with a
          320 bit hash. It is intended for applications that require
@@ -352,13 +351,13 @@ config CRYPTO_RMD320
 
 config CRYPTO_SHA1
        tristate "SHA1 digest algorithm"
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2).
 
 config CRYPTO_SHA256
        tristate "SHA224 and SHA256 digest algorithm"
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          SHA256 secure hash standard (DFIPS 180-2).
 
@@ -370,7 +369,7 @@ config CRYPTO_SHA256
 
 config CRYPTO_SHA512
        tristate "SHA384 and SHA512 digest algorithms"
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          SHA512 secure hash standard (DFIPS 180-2).
 
@@ -382,7 +381,7 @@ config CRYPTO_SHA512
 
 config CRYPTO_TGR192
        tristate "Tiger digest algorithms"
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          Tiger hash algorithm 192, 160 and 128-bit hashes
 
@@ -395,7 +394,7 @@ config CRYPTO_TGR192
 
 config CRYPTO_WP512
        tristate "Whirlpool digest algorithms"
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          Whirlpool hash algorithm 512, 384 and 256-bit hashes
 
index cd4a4ed..46b08bf 100644 (file)
@@ -22,6 +22,7 @@ obj-$(CONFIG_CRYPTO_SEQIV) += seqiv.o
 
 crypto_hash-objs := hash.o
 crypto_hash-objs += ahash.o
+crypto_hash-objs += shash.o
 obj-$(CONFIG_CRYPTO_HASH2) += crypto_hash.o
 
 cryptomgr-objs := algboss.o testmgr.o
index 136dc98..b8b66ec 100644 (file)
@@ -60,102 +60,1068 @@ static inline u8 byte(const u32 x, const unsigned n)
        return x >> (n << 3);
 }
 
-static u8 pow_tab[256] __initdata;
-static u8 log_tab[256] __initdata;
-static u8 sbx_tab[256] __initdata;
-static u8 isb_tab[256] __initdata;
-static u32 rco_tab[10];
-
-u32 crypto_ft_tab[4][256];
-u32 crypto_fl_tab[4][256];
-u32 crypto_it_tab[4][256];
-u32 crypto_il_tab[4][256];
-
-EXPORT_SYMBOL_GPL(crypto_ft_tab);
-EXPORT_SYMBOL_GPL(crypto_fl_tab);
-EXPORT_SYMBOL_GPL(crypto_it_tab);
-EXPORT_SYMBOL_GPL(crypto_il_tab);
-
-static inline u8 __init f_mult(u8 a, u8 b)
-{
-       u8 aa = log_tab[a], cc = aa + log_tab[b];
-
-       return pow_tab[cc + (cc < aa ? 1 : 0)];
-}
-
-#define ff_mult(a, b)  (a && b ? f_mult(a, b) : 0)
-
-static void __init gen_tabs(void)
-{
-       u32 i, t;
-       u8 p, q;
-
-       /*
-        * log and power tables for GF(2**8) finite field with
-        * 0x011b as modular polynomial - the simplest primitive
-        * root is 0x03, used here to generate the tables
-        */
-
-       for (i = 0, p = 1; i < 256; ++i) {
-               pow_tab[i] = (u8) p;
-               log_tab[p] = (u8) i;
-
-               p ^= (p << 1) ^ (p & 0x80 ? 0x01b : 0);
+static const u32 rco_tab[10] = { 1, 2, 4, 8, 16, 32, 64, 128, 27, 54 };
+
+const u32 crypto_ft_tab[4][256] = {
+       {
+               0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6,
+               0x0df2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591,
+               0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56,
+               0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec,
+               0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa,
+               0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb,
+               0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45,
+               0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b,
+               0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c,
+               0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83,
+               0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9,
+               0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a,
+               0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d,
+               0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f,
+               0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
+               0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea,
+               0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34,
+               0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b,
+               0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d,
+               0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413,
+               0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1,
+               0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6,
+               0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972,
+               0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85,
+               0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed,
+               0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511,
+               0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe,
+               0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b,
+               0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05,
+               0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
+               0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142,
+               0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf,
+               0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3,
+               0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e,
+               0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a,
+               0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6,
+               0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3,
+               0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b,
+               0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428,
+               0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad,
+               0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14,
+               0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8,
+               0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4,
+               0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2,
+               0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
+               0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949,
+               0xb46c6cd8, 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf,
+               0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810,
+               0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c,
+               0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697,
+               0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e,
+               0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f,
+               0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc,
+               0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c,
+               0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969,
+               0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27,
+               0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122,
+               0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433,
+               0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9,
+               0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
+               0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a,
+               0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0,
+               0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e,
+               0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c,
+       }, {
+               0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d,
+               0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154,
+               0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d,
+               0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a,
+               0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87,
+               0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b,
+               0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea,
+               0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b,
+               0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a,
+               0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f,
+               0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908,
+               0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f,
+               0x0404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e,
+               0x18183028, 0x969637a1, 0x05050a0f, 0x9a9a2fb5,
+               0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d,
+               0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f,
+               0x0909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e,
+               0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb,
+               0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce,
+               0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397,
+               0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c,
+               0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed,
+               0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b,
+               0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a,
+               0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16,
+               0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194,
+               0x45458acf, 0xf9f9e910, 0x02020406, 0x7f7ffe81,
+               0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3,
+               0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a,
+               0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104,
+               0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263,
+               0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d,
+               0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f,
+               0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39,
+               0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47,
+               0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695,
+               0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f,
+               0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83,
+               0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c,
+               0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76,
+               0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e,
+               0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4,
+               0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6,
+               0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b,
+               0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7,
+               0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0,
+               0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25,
+               0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018,
+               0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72,
+               0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751,
+               0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21,
+               0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85,
+               0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa,
+               0x484890d8, 0x03030605, 0xf6f6f701, 0x0e0e1c12,
+               0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0,
+               0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9,
+               0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233,
+               0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7,
+               0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920,
+               0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a,
+               0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17,
+               0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8,
+               0x414182c3, 0x999929b0, 0x2d2d5a77, 0x0f0f1e11,
+               0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a,
+       }, {
+               0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b,
+               0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5,
+               0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b,
+               0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76,
+               0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d,
+               0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0,
+               0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf,
+               0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0,
+               0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26,
+               0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc,
+               0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1,
+               0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15,
+               0x04080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3,
+               0x18302818, 0x9637a196, 0x050a0f05, 0x9a2fb59a,
+               0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2,
+               0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75,
+               0x09121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a,
+               0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0,
+               0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3,
+               0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784,
+               0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced,
+               0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b,
+               0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39,
+               0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf,
+               0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb,
+               0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485,
+               0x458acf45, 0xf9e910f9, 0x02040602, 0x7ffe817f,
+               0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8,
+               0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f,
+               0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5,
+               0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321,
+               0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2,
+               0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec,
+               0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917,
+               0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d,
+               0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573,
+               0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc,
+               0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388,
+               0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14,
+               0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db,
+               0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a,
+               0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c,
+               0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662,
+               0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79,
+               0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d,
+               0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9,
+               0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea,
+               0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808,
+               0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e,
+               0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6,
+               0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f,
+               0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a,
+               0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66,
+               0x4890d848, 0x03060503, 0xf6f701f6, 0x0e1c120e,
+               0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9,
+               0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e,
+               0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311,
+               0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794,
+               0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9,
+               0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf,
+               0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d,
+               0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868,
+               0x4182c341, 0x9929b099, 0x2d5a772d, 0x0f1e110f,
+               0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16,
+       }, {
+               0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b,
+               0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5,
+               0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b,
+               0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676,
+               0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d,
+               0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0,
+               0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf,
+               0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0,
+               0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626,
+               0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc,
+               0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1,
+               0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515,
+               0x080c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3,
+               0x30281818, 0x37a19696, 0x0a0f0505, 0x2fb59a9a,
+               0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2,
+               0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575,
+               0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a,
+               0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0,
+               0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3,
+               0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484,
+               0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded,
+               0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b,
+               0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939,
+               0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf,
+               0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb,
+               0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585,
+               0x8acf4545, 0xe910f9f9, 0x04060202, 0xfe817f7f,
+               0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8,
+               0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x058a8f8f,
+               0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5,
+               0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121,
+               0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2,
+               0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec,
+               0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717,
+               0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d,
+               0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373,
+               0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc,
+               0x44662222, 0x547e2a2a, 0x3bab9090, 0x0b838888,
+               0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414,
+               0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb,
+               0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a,
+               0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c,
+               0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262,
+               0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979,
+               0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d,
+               0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9,
+               0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea,
+               0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808,
+               0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e,
+               0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6,
+               0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f,
+               0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a,
+               0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666,
+               0x90d84848, 0x06050303, 0xf701f6f6, 0x1c120e0e,
+               0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9,
+               0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e,
+               0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111,
+               0xd2bb6969, 0xa970d9d9, 0x07898e8e, 0x33a79494,
+               0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9,
+               0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf,
+               0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d,
+               0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868,
+               0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f,
+               0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616,
        }
+};
 
-       log_tab[1] = 0;
-
-       for (i = 0, p = 1; i < 10; ++i) {
-               rco_tab[i] = p;
-
-               p = (p << 1) ^ (p & 0x80 ? 0x01b : 0);
+const u32 crypto_fl_tab[4][256] = {
+       {
+               0x00000063, 0x0000007c, 0x00000077, 0x0000007b,
+               0x000000f2, 0x0000006b, 0x0000006f, 0x000000c5,
+               0x00000030, 0x00000001, 0x00000067, 0x0000002b,
+               0x000000fe, 0x000000d7, 0x000000ab, 0x00000076,
+               0x000000ca, 0x00000082, 0x000000c9, 0x0000007d,
+               0x000000fa, 0x00000059, 0x00000047, 0x000000f0,
+               0x000000ad, 0x000000d4, 0x000000a2, 0x000000af,
+               0x0000009c, 0x000000a4, 0x00000072, 0x000000c0,
+               0x000000b7, 0x000000fd, 0x00000093, 0x00000026,
+               0x00000036, 0x0000003f, 0x000000f7, 0x000000cc,
+               0x00000034, 0x000000a5, 0x000000e5, 0x000000f1,
+               0x00000071, 0x000000d8, 0x00000031, 0x00000015,
+               0x00000004, 0x000000c7, 0x00000023, 0x000000c3,
+               0x00000018, 0x00000096, 0x00000005, 0x0000009a,
+               0x00000007, 0x00000012, 0x00000080, 0x000000e2,
+               0x000000eb, 0x00000027, 0x000000b2, 0x00000075,
+               0x00000009, 0x00000083, 0x0000002c, 0x0000001a,
+               0x0000001b, 0x0000006e, 0x0000005a, 0x000000a0,
+               0x00000052, 0x0000003b, 0x000000d6, 0x000000b3,
+               0x00000029, 0x000000e3, 0x0000002f, 0x00000084,
+               0x00000053, 0x000000d1, 0x00000000, 0x000000ed,
+               0x00000020, 0x000000fc, 0x000000b1, 0x0000005b,
+               0x0000006a, 0x000000cb, 0x000000be, 0x00000039,
+               0x0000004a, 0x0000004c, 0x00000058, 0x000000cf,
+               0x000000d0, 0x000000ef, 0x000000aa, 0x000000fb,
+               0x00000043, 0x0000004d, 0x00000033, 0x00000085,
+               0x00000045, 0x000000f9, 0x00000002, 0x0000007f,
+               0x00000050, 0x0000003c, 0x0000009f, 0x000000a8,
+               0x00000051, 0x000000a3, 0x00000040, 0x0000008f,
+               0x00000092, 0x0000009d, 0x00000038, 0x000000f5,
+               0x000000bc, 0x000000b6, 0x000000da, 0x00000021,
+               0x00000010, 0x000000ff, 0x000000f3, 0x000000d2,
+               0x000000cd, 0x0000000c, 0x00000013, 0x000000ec,
+               0x0000005f, 0x00000097, 0x00000044, 0x00000017,
+               0x000000c4, 0x000000a7, 0x0000007e, 0x0000003d,
+               0x00000064, 0x0000005d, 0x00000019, 0x00000073,
+               0x00000060, 0x00000081, 0x0000004f, 0x000000dc,
+               0x00000022, 0x0000002a, 0x00000090, 0x00000088,
+               0x00000046, 0x000000ee, 0x000000b8, 0x00000014,
+               0x000000de, 0x0000005e, 0x0000000b, 0x000000db,
+               0x000000e0, 0x00000032, 0x0000003a, 0x0000000a,
+               0x00000049, 0x00000006, 0x00000024, 0x0000005c,
+               0x000000c2, 0x000000d3, 0x000000ac, 0x00000062,
+               0x00000091, 0x00000095, 0x000000e4, 0x00000079,
+               0x000000e7, 0x000000c8, 0x00000037, 0x0000006d,
+               0x0000008d, 0x000000d5, 0x0000004e, 0x000000a9,
+               0x0000006c, 0x00000056, 0x000000f4, 0x000000ea,
+               0x00000065, 0x0000007a, 0x000000ae, 0x00000008,
+               0x000000ba, 0x00000078, 0x00000025, 0x0000002e,
+               0x0000001c, 0x000000a6, 0x000000b4, 0x000000c6,
+               0x000000e8, 0x000000dd, 0x00000074, 0x0000001f,
+               0x0000004b, 0x000000bd, 0x0000008b, 0x0000008a,
+               0x00000070, 0x0000003e, 0x000000b5, 0x00000066,
+               0x00000048, 0x00000003, 0x000000f6, 0x0000000e,
+               0x00000061, 0x00000035, 0x00000057, 0x000000b9,
+               0x00000086, 0x000000c1, 0x0000001d, 0x0000009e,
+               0x000000e1, 0x000000f8, 0x00000098, 0x00000011,
+               0x00000069, 0x000000d9, 0x0000008e, 0x00000094,
+               0x0000009b, 0x0000001e, 0x00000087, 0x000000e9,
+               0x000000ce, 0x00000055, 0x00000028, 0x000000df,
+               0x0000008c, 0x000000a1, 0x00000089, 0x0000000d,
+               0x000000bf, 0x000000e6, 0x00000042, 0x00000068,
+               0x00000041, 0x00000099, 0x0000002d, 0x0000000f,
+               0x000000b0, 0x00000054, 0x000000bb, 0x00000016,
+       }, {
+               0x00006300, 0x00007c00, 0x00007700, 0x00007b00,
+               0x0000f200, 0x00006b00, 0x00006f00, 0x0000c500,
+               0x00003000, 0x00000100, 0x00006700, 0x00002b00,
+               0x0000fe00, 0x0000d700, 0x0000ab00, 0x00007600,
+               0x0000ca00, 0x00008200, 0x0000c900, 0x00007d00,
+               0x0000fa00, 0x00005900, 0x00004700, 0x0000f000,
+               0x0000ad00, 0x0000d400, 0x0000a200, 0x0000af00,
+               0x00009c00, 0x0000a400, 0x00007200, 0x0000c000,
+               0x0000b700, 0x0000fd00, 0x00009300, 0x00002600,
+               0x00003600, 0x00003f00, 0x0000f700, 0x0000cc00,
+               0x00003400, 0x0000a500, 0x0000e500, 0x0000f100,
+               0x00007100, 0x0000d800, 0x00003100, 0x00001500,
+               0x00000400, 0x0000c700, 0x00002300, 0x0000c300,
+               0x00001800, 0x00009600, 0x00000500, 0x00009a00,
+               0x00000700, 0x00001200, 0x00008000, 0x0000e200,
+               0x0000eb00, 0x00002700, 0x0000b200, 0x00007500,
+               0x00000900, 0x00008300, 0x00002c00, 0x00001a00,
+               0x00001b00, 0x00006e00, 0x00005a00, 0x0000a000,
+               0x00005200, 0x00003b00, 0x0000d600, 0x0000b300,
+               0x00002900, 0x0000e300, 0x00002f00, 0x00008400,
+               0x00005300, 0x0000d100, 0x00000000, 0x0000ed00,
+               0x00002000, 0x0000fc00, 0x0000b100, 0x00005b00,
+               0x00006a00, 0x0000cb00, 0x0000be00, 0x00003900,
+               0x00004a00, 0x00004c00, 0x00005800, 0x0000cf00,
+               0x0000d000, 0x0000ef00, 0x0000aa00, 0x0000fb00,
+               0x00004300, 0x00004d00, 0x00003300, 0x00008500,
+               0x00004500, 0x0000f900, 0x00000200, 0x00007f00,
+               0x00005000, 0x00003c00, 0x00009f00, 0x0000a800,
+               0x00005100, 0x0000a300, 0x00004000, 0x00008f00,
+               0x00009200, 0x00009d00, 0x00003800, 0x0000f500,
+               0x0000bc00, 0x0000b600, 0x0000da00, 0x00002100,
+               0x00001000, 0x0000ff00, 0x0000f300, 0x0000d200,
+               0x0000cd00, 0x00000c00, 0x00001300, 0x0000ec00,
+               0x00005f00, 0x00009700, 0x00004400, 0x00001700,
+               0x0000c400, 0x0000a700, 0x00007e00, 0x00003d00,
+               0x00006400, 0x00005d00, 0x00001900, 0x00007300,
+               0x00006000, 0x00008100, 0x00004f00, 0x0000dc00,
+               0x00002200, 0x00002a00, 0x00009000, 0x00008800,
+               0x00004600, 0x0000ee00, 0x0000b800, 0x00001400,
+               0x0000de00, 0x00005e00, 0x00000b00, 0x0000db00,
+               0x0000e000, 0x00003200, 0x00003a00, 0x00000a00,
+               0x00004900, 0x00000600, 0x00002400, 0x00005c00,
+               0x0000c200, 0x0000d300, 0x0000ac00, 0x00006200,
+               0x00009100, 0x00009500, 0x0000e400, 0x00007900,
+               0x0000e700, 0x0000c800, 0x00003700, 0x00006d00,
+               0x00008d00, 0x0000d500, 0x00004e00, 0x0000a900,
+               0x00006c00, 0x00005600, 0x0000f400, 0x0000ea00,
+               0x00006500, 0x00007a00, 0x0000ae00, 0x00000800,
+               0x0000ba00, 0x00007800, 0x00002500, 0x00002e00,
+               0x00001c00, 0x0000a600, 0x0000b400, 0x0000c600,
+               0x0000e800, 0x0000dd00, 0x00007400, 0x00001f00,
+               0x00004b00, 0x0000bd00, 0x00008b00, 0x00008a00,
+               0x00007000, 0x00003e00, 0x0000b500, 0x00006600,
+               0x00004800, 0x00000300, 0x0000f600, 0x00000e00,
+               0x00006100, 0x00003500, 0x00005700, 0x0000b900,
+               0x00008600, 0x0000c100, 0x00001d00, 0x00009e00,
+               0x0000e100, 0x0000f800, 0x00009800, 0x00001100,
+               0x00006900, 0x0000d900, 0x00008e00, 0x00009400,
+               0x00009b00, 0x00001e00, 0x00008700, 0x0000e900,
+               0x0000ce00, 0x00005500, 0x00002800, 0x0000df00,
+               0x00008c00, 0x0000a100, 0x00008900, 0x00000d00,
+               0x0000bf00, 0x0000e600, 0x00004200, 0x00006800,
+               0x00004100, 0x00009900, 0x00002d00, 0x00000f00,
+               0x0000b000, 0x00005400, 0x0000bb00, 0x00001600,
+       }, {
+               0x00630000, 0x007c0000, 0x00770000, 0x007b0000,
+               0x00f20000, 0x006b0000, 0x006f0000, 0x00c50000,
+               0x00300000, 0x00010000, 0x00670000, 0x002b0000,
+               0x00fe0000, 0x00d70000, 0x00ab0000, 0x00760000,
+               0x00ca0000, 0x00820000, 0x00c90000, 0x007d0000,
+               0x00fa0000, 0x00590000, 0x00470000, 0x00f00000,
+               0x00ad0000, 0x00d40000, 0x00a20000, 0x00af0000,
+               0x009c0000, 0x00a40000, 0x00720000, 0x00c00000,
+               0x00b70000, 0x00fd0000, 0x00930000, 0x00260000,
+               0x00360000, 0x003f0000, 0x00f70000, 0x00cc0000,
+               0x00340000, 0x00a50000, 0x00e50000, 0x00f10000,
+               0x00710000, 0x00d80000, 0x00310000, 0x00150000,
+               0x00040000, 0x00c70000, 0x00230000, 0x00c30000,
+               0x00180000, 0x00960000, 0x00050000, 0x009a0000,
+               0x00070000, 0x00120000, 0x00800000, 0x00e20000,
+               0x00eb0000, 0x00270000, 0x00b20000, 0x00750000,
+               0x00090000, 0x00830000, 0x002c0000, 0x001a0000,
+               0x001b0000, 0x006e0000, 0x005a0000, 0x00a00000,
+               0x00520000, 0x003b0000, 0x00d60000, 0x00b30000,
+               0x00290000, 0x00e30000, 0x002f0000, 0x00840000,
+               0x00530000, 0x00d10000, 0x00000000, 0x00ed0000,
+               0x00200000, 0x00fc0000, 0x00b10000, 0x005b0000,
+               0x006a0000, 0x00cb0000, 0x00be0000, 0x00390000,
+               0x004a0000, 0x004c0000, 0x00580000, 0x00cf0000,
+               0x00d00000, 0x00ef0000, 0x00aa0000, 0x00fb0000,
+               0x00430000, 0x004d0000, 0x00330000, 0x00850000,
+               0x00450000, 0x00f90000, 0x00020000, 0x007f0000,
+               0x00500000, 0x003c0000, 0x009f0000, 0x00a80000,
+               0x00510000, 0x00a30000, 0x00400000, 0x008f0000,
+               0x00920000, 0x009d0000, 0x00380000, 0x00f50000,
+               0x00bc0000, 0x00b60000, 0x00da0000, 0x00210000,
+               0x00100000, 0x00ff0000, 0x00f30000, 0x00d20000,
+               0x00cd0000, 0x000c0000, 0x00130000, 0x00ec0000,
+               0x005f0000, 0x00970000, 0x00440000, 0x00170000,
+               0x00c40000, 0x00a70000, 0x007e0000, 0x003d0000,
+               0x00640000, 0x005d0000, 0x00190000, 0x00730000,
+               0x00600000, 0x00810000, 0x004f0000, 0x00dc0000,
+               0x00220000, 0x002a0000, 0x00900000, 0x00880000,
+               0x00460000, 0x00ee0000, 0x00b80000, 0x00140000,
+               0x00de0000, 0x005e0000, 0x000b0000, 0x00db0000,
+               0x00e00000, 0x00320000, 0x003a0000, 0x000a0000,
+               0x00490000, 0x00060000, 0x00240000, 0x005c0000,
+               0x00c20000, 0x00d30000, 0x00ac0000, 0x00620000,
+               0x00910000, 0x00950000, 0x00e40000, 0x00790000,
+               0x00e70000, 0x00c80000, 0x00370000, 0x006d0000,
+               0x008d0000, 0x00d50000, 0x004e0000, 0x00a90000,
+               0x006c0000, 0x00560000, 0x00f40000, 0x00ea0000,
+               0x00650000, 0x007a0000, 0x00ae0000, 0x00080000,
+               0x00ba0000, 0x00780000, 0x00250000, 0x002e0000,
+               0x001c0000, 0x00a60000, 0x00b40000, 0x00c60000,
+               0x00e80000, 0x00dd0000, 0x00740000, 0x001f0000,
+               0x004b0000, 0x00bd0000, 0x008b0000, 0x008a0000,
+               0x00700000, 0x003e0000, 0x00b50000, 0x00660000,
+               0x00480000, 0x00030000, 0x00f60000, 0x000e0000,
+               0x00610000, 0x00350000, 0x00570000, 0x00b90000,
+               0x00860000, 0x00c10000, 0x001d0000, 0x009e0000,
+               0x00e10000, 0x00f80000, 0x00980000, 0x00110000,
+               0x00690000, 0x00d90000, 0x008e0000, 0x00940000,
+               0x009b0000, 0x001e0000, 0x00870000, 0x00e90000,
+               0x00ce0000, 0x00550000, 0x00280000, 0x00df0000,
+               0x008c0000, 0x00a10000, 0x00890000, 0x000d0000,
+               0x00bf0000, 0x00e60000, 0x00420000, 0x00680000,
+               0x00410000, 0x00990000, 0x002d0000, 0x000f0000,
+               0x00b00000, 0x00540000, 0x00bb0000, 0x00160000,
+       }, {
+               0x63000000, 0x7c000000, 0x77000000, 0x7b000000,
+               0xf2000000, 0x6b000000, 0x6f000000, 0xc5000000,
+               0x30000000, 0x01000000, 0x67000000, 0x2b000000,
+               0xfe000000, 0xd7000000, 0xab000000, 0x76000000,
+               0xca000000, 0x82000000, 0xc9000000, 0x7d000000,
+               0xfa000000, 0x59000000, 0x47000000, 0xf0000000,
+               0xad000000, 0xd4000000, 0xa2000000, 0xaf000000,
+               0x9c000000, 0xa4000000, 0x72000000, 0xc0000000,
+               0xb7000000, 0xfd000000, 0x93000000, 0x26000000,
+               0x36000000, 0x3f000000, 0xf7000000, 0xcc000000,
+               0x34000000, 0xa5000000, 0xe5000000, 0xf1000000,
+               0x71000000, 0xd8000000, 0x31000000, 0x15000000,
+               0x04000000, 0xc7000000, 0x23000000, 0xc3000000,
+               0x18000000, 0x96000000, 0x05000000, 0x9a000000,
+               0x07000000, 0x12000000, 0x80000000, 0xe2000000,
+               0xeb000000, 0x27000000, 0xb2000000, 0x75000000,
+               0x09000000, 0x83000000, 0x2c000000, 0x1a000000,
+               0x1b000000, 0x6e000000, 0x5a000000, 0xa0000000,
+               0x52000000, 0x3b000000, 0xd6000000, 0xb3000000,
+               0x29000000, 0xe3000000, 0x2f000000, 0x84000000,
+               0x53000000, 0xd1000000, 0x00000000, 0xed000000,
+               0x20000000, 0xfc000000, 0xb1000000, 0x5b000000,
+               0x6a000000, 0xcb000000, 0xbe000000, 0x39000000,
+               0x4a000000, 0x4c000000, 0x58000000, 0xcf000000,
+               0xd0000000, 0xef000000, 0xaa000000, 0xfb000000,
+               0x43000000, 0x4d000000, 0x33000000, 0x85000000,
+               0x45000000, 0xf9000000, 0x02000000, 0x7f000000,
+               0x50000000, 0x3c000000, 0x9f000000, 0xa8000000,
+               0x51000000, 0xa3000000, 0x40000000, 0x8f000000,
+               0x92000000, 0x9d000000, 0x38000000, 0xf5000000,
+               0xbc000000, 0xb6000000, 0xda000000, 0x21000000,
+               0x10000000, 0xff000000, 0xf3000000, 0xd2000000,
+               0xcd000000, 0x0c000000, 0x13000000, 0xec000000,
+               0x5f000000, 0x97000000, 0x44000000, 0x17000000,
+               0xc4000000, 0xa7000000, 0x7e000000, 0x3d000000,
+               0x64000000, 0x5d000000, 0x19000000, 0x73000000,
+               0x60000000, 0x81000000, 0x4f000000, 0xdc000000,
+               0x22000000, 0x2a000000, 0x90000000, 0x88000000,
+               0x46000000, 0xee000000, 0xb8000000, 0x14000000,
+               0xde000000, 0x5e000000, 0x0b000000, 0xdb000000,
+               0xe0000000, 0x32000000, 0x3a000000, 0x0a000000,
+               0x49000000, 0x06000000, 0x24000000, 0x5c000000,
+               0xc2000000, 0xd3000000, 0xac000000, 0x62000000,
+               0x91000000, 0x95000000, 0xe4000000, 0x79000000,
+               0xe7000000, 0xc8000000, 0x37000000, 0x6d000000,
+               0x8d000000, 0xd5000000, 0x4e000000, 0xa9000000,
+               0x6c000000, 0x56000000, 0xf4000000, 0xea000000,
+               0x65000000, 0x7a000000, 0xae000000, 0x08000000,
+               0xba000000, 0x78000000, 0x25000000, 0x2e000000,
+               0x1c000000, 0xa6000000, 0xb4000000, 0xc6000000,
+               0xe8000000, 0xdd000000, 0x74000000, 0x1f000000,
+               0x4b000000, 0xbd000000, 0x8b000000, 0x8a000000,
+               0x70000000, 0x3e000000, 0xb5000000, 0x66000000,
+               0x48000000, 0x03000000, 0xf6000000, 0x0e000000,
+               0x61000000, 0x35000000, 0x57000000, 0xb9000000,
+               0x86000000, 0xc1000000, 0x1d000000, 0x9e000000,
+               0xe1000000, 0xf8000000, 0x98000000, 0x11000000,
+               0x69000000, 0xd9000000, 0x8e000000, 0x94000000,
+               0x9b000000, 0x1e000000, 0x87000000, 0xe9000000,
+               0xce000000, 0x55000000, 0x28000000, 0xdf000000,
+               0x8c000000, 0xa1000000, 0x89000000, 0x0d000000,
+               0xbf000000, 0xe6000000, 0x42000000, 0x68000000,
+               0x41000000, 0x99000000, 0x2d000000, 0x0f000000,
+               0xb0000000, 0x54000000, 0xbb000000, 0x16000000,
        }
+};
 
-       for (i = 0; i < 256; ++i) {
-               p = (i ? pow_tab[255 - log_tab[i]] : 0);
-               q = ((p >> 7) | (p << 1)) ^ ((p >> 6) | (p << 2));
-               p ^= 0x63 ^ q ^ ((q >> 6) | (q << 2));
-               sbx_tab[i] = p;
-               isb_tab[p] = (u8) i;
+const u32 crypto_it_tab[4][256] = {
+       {
+               0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a,
+               0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b,
+               0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5,
+               0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5,
+               0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d,
+               0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b,
+               0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295,
+               0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e,
+               0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927,
+               0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d,
+               0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362,
+               0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9,
+               0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52,
+               0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566,
+               0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
+               0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed,
+               0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e,
+               0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4,
+               0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4,
+               0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd,
+               0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d,
+               0xb58d5491, 0x055dc471, 0x6fd40604, 0xff155060,
+               0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967,
+               0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879,
+               0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000,
+               0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c,
+               0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36,
+               0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624,
+               0xb1670a0c, 0x0fe75793, 0xd296eeb4, 0x9e919b1b,
+               0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c,
+               0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12,
+               0x0b0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14,
+               0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3,
+               0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b,
+               0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8,
+               0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684,
+               0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7,
+               0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177,
+               0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947,
+               0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322,
+               0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498,
+               0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f,
+               0xe49d3a2c, 0x0d927850, 0x9bcc5f6a, 0x62467e54,
+               0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382,
+               0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf,
+               0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb,
+               0x097826cd, 0xf418596e, 0x01b79aec, 0xa89a4f83,
+               0x656e95e6, 0x7ee6ffaa, 0x08cfbc21, 0xe6e815ef,
+               0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029,
+               0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235,
+               0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733,
+               0x4a9804f1, 0xf7daec41, 0x0e50cd7f, 0x2ff69117,
+               0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4,
+               0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546,
+               0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb,
+               0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d,
+               0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb,
+               0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a,
+               0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773,
+               0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
+               0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2,
+               0x72c31d16, 0x0c25e2bc, 0x8b493c28, 0x41950dff,
+               0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664,
+               0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0,
+       }, {
+               0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96,
+               0x6bab3bcb, 0x459d1ff1, 0x58faacab, 0x03e34b93,
+               0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525,
+               0xd7e54ffc, 0xcb2ac5d7, 0x44352680, 0xa362b58f,
+               0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1,
+               0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6,
+               0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da,
+               0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44,
+               0x89c2756a, 0x798ef478, 0x3e58996b, 0x71b927dd,
+               0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4,
+               0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245,
+               0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994,
+               0x68487058, 0xfd458f19, 0x6cde9487, 0xf87b52b7,
+               0xd373ab23, 0x024b72e2, 0x8f1fe357, 0xab55662a,
+               0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5,
+               0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c,
+               0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1,
+               0xf4da65cd, 0xbe0506d5, 0x6234d11f, 0xfea6c48a,
+               0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475,
+               0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51,
+               0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46,
+               0x8d5491b5, 0x5dc47105, 0xd406046f, 0x155060ff,
+               0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777,
+               0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db,
+               0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000,
+               0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e,
+               0xff0efdfb, 0x38850f56, 0xd5ae3d1e, 0x392d3627,
+               0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a,
+               0x670a0cb1, 0xe757930f, 0x96eeb4d2, 0x919b1b9e,
+               0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16,
+               0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d,
+               0x0d090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8,
+               0x19f15785, 0x0775af4c, 0xdd99eebb, 0x607fa3fd,
+               0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34,
+               0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863,
+               0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420,
+               0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d,
+               0x2f9e1d4b, 0x30b2dcf3, 0x52860dec, 0xe3c177d0,
+               0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722,
+               0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef,
+               0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836,
+               0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4,
+               0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462,
+               0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5,
+               0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3,
+               0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b,
+               0x7826cd09, 0x18596ef4, 0xb79aec01, 0x9a4f83a8,
+               0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6,
+               0x9be7bad9, 0x366f4ace, 0x099fead4, 0x7cb029d6,
+               0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0,
+               0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315,
+               0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f,
+               0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x0496e4df,
+               0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, 0x5165467f,
+               0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e,
+               0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13,
+               0x61d79a8c, 0x0ca1377a, 0x14f8598e, 0x3c13eb89,
+               0x27a9ceee, 0xc961b735, 0xe51ce1ed, 0xb1477a3c,
+               0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf,
+               0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886,
+               0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f,
+               0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41,
+               0x01a83971, 0xb30c08de, 0xe4b4d89c, 0xc1566490,
+               0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042,
+       }, {
+               0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e,
+               0xab3bcb6b, 0x9d1ff145, 0xfaacab58, 0xe34b9303,
+               0x302055fa, 0x76adf66d, 0xcc889176, 0x02f5254c,
+               0xe54ffcd7, 0x2ac5d7cb, 0x35268044, 0x62b58fa3,
+               0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0,
+               0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9,
+               0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59,
+               0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8,
+               0xc2756a89, 0x8ef47879, 0x58996b3e, 0xb927dd71,
+               0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a,
+               0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f,
+               0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b,
+               0x48705868, 0x458f19fd, 0xde94876c, 0x7b52b7f8,
+               0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab,
+               0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508,
+               0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82,
+               0xcf8a2b1c, 0x79a792b4, 0x07f3f0f2, 0x694ea1e2,
+               0xda65cdf4, 0x0506d5be, 0x34d11f62, 0xa6c48afe,
+               0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb,
+               0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110,
+               0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd,
+               0x5491b58d, 0xc471055d, 0x06046fd4, 0x5060ff15,
+               0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e,
+               0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee,
+               0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000,
+               0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72,
+               0x0efdfbff, 0x850f5638, 0xae3d1ed5, 0x2d362739,
+               0x0f0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e,
+               0x0a0cb167, 0x57930fe7, 0xeeb4d296, 0x9b1b9e91,
+               0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a,
+               0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17,
+               0x090e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9,
+               0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60,
+               0x01f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e,
+               0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1,
+               0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011,
+               0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1,
+               0x9e1d4b2f, 0xb2dcf330, 0x860dec52, 0xc177d0e3,
+               0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264,
+               0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90,
+               0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b,
+               0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf,
+               0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246,
+               0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af,
+               0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312,
+               0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb,
+               0x26cd0978, 0x596ef418, 0x9aec01b7, 0x4f83a89a,
+               0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8,
+               0xe7bad99b, 0x6f4ace36, 0x9fead409, 0xb029d67c,
+               0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066,
+               0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8,
+               0x04f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6,
+               0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04,
+               0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, 0x65467f51,
+               0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41,
+               0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347,
+               0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c,
+               0xa9ceee27, 0x61b735c9, 0x1ce1ede5, 0x477a3cb1,
+               0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37,
+               0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db,
+               0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40,
+               0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0x0dff4195,
+               0xa8397101, 0x0c08deb3, 0xb4d89ce4, 0x566490c1,
+               0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257,
+       }, {
+               0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27,
+               0x3bcb6bab, 0x1ff1459d, 0xacab58fa, 0x4b9303e3,
+               0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02,
+               0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, 0xb58fa362,
+               0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe,
+               0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3,
+               0x03e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952,
+               0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9,
+               0x756a89c2, 0xf478798e, 0x996b3e58, 0x27dd71b9,
+               0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace,
+               0x63184adf, 0xe582311a, 0x97603351, 0x62457f53,
+               0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08,
+               0x70586848, 0x8f19fd45, 0x94876cde, 0x52b7f87b,
+               0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55,
+               0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837,
+               0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216,
+               0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269,
+               0x65cdf4da, 0x06d5be05, 0xd11f6234, 0xc48afea6,
+               0x349d532e, 0xa2a055f3, 0x0532e18a, 0xa475ebf6,
+               0x0b39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e,
+               0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6,
+               0x91b58d54, 0x71055dc4, 0x046fd406, 0x60ff1550,
+               0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9,
+               0xb0bd42e8, 0x07888b89, 0xe7385b19, 0x79dbeec8,
+               0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000,
+               0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a,
+               0xfdfbff0e, 0x0f563885, 0x3d1ed5ae, 0x3627392d,
+               0x0a64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36,
+               0x0cb1670a, 0x930fe757, 0xb4d296ee, 0x1b9e919b,
+               0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12,
+               0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b,
+               0x0e0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e,
+               0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f,
+               0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb,
+               0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4,
+               0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6,
+               0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129,
+               0x1d4b2f9e, 0xdcf330b2, 0x0dec5286, 0x77d0e3c1,
+               0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9,
+               0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033,
+               0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4,
+               0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad,
+               0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e,
+               0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3,
+               0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225,
+               0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b,
+               0xcd097826, 0x6ef41859, 0xec01b79a, 0x83a89a4f,
+               0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815,
+               0xbad99be7, 0x4ace366f, 0xead4099f, 0x29d67cb0,
+               0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2,
+               0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7,
+               0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691,
+               0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496,
+               0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, 0x467f5165,
+               0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b,
+               0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6,
+               0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13,
+               0xceee27a9, 0xb735c961, 0xe1ede51c, 0x7a3cb147,
+               0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7,
+               0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44,
+               0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3,
+               0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d,
+               0x397101a8, 0x08deb30c, 0xd89ce4b4, 0x6490c156,
+               0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8,
        }
+};
 
-       for (i = 0; i < 256; ++i) {
-               p = sbx_tab[i];
-
-               t = p;
-               crypto_fl_tab[0][i] = t;
-               crypto_fl_tab[1][i] = rol32(t, 8);
-               crypto_fl_tab[2][i] = rol32(t, 16);
-               crypto_fl_tab[3][i] = rol32(t, 24);
-
-               t = ((u32) ff_mult(2, p)) |
-                   ((u32) p << 8) |
-                   ((u32) p << 16) | ((u32) ff_mult(3, p) << 24);
-
-               crypto_ft_tab[0][i] = t;
-               crypto_ft_tab[1][i] = rol32(t, 8);
-               crypto_ft_tab[2][i] = rol32(t, 16);
-               crypto_ft_tab[3][i] = rol32(t, 24);
-
-               p = isb_tab[i];
-
-               t = p;
-               crypto_il_tab[0][i] = t;
-               crypto_il_tab[1][i] = rol32(t, 8);
-               crypto_il_tab[2][i] = rol32(t, 16);
-               crypto_il_tab[3][i] = rol32(t, 24);
-
-               t = ((u32) ff_mult(14, p)) |
-                   ((u32) ff_mult(9, p) << 8) |
-                   ((u32) ff_mult(13, p) << 16) |
-                   ((u32) ff_mult(11, p) << 24);
-
-               crypto_it_tab[0][i] = t;
-               crypto_it_tab[1][i] = rol32(t, 8);
-               crypto_it_tab[2][i] = rol32(t, 16);
-               crypto_it_tab[3][i] = rol32(t, 24);
+const u32 crypto_il_tab[4][256] = {
+       {
+               0x00000052, 0x00000009, 0x0000006a, 0x000000d5,
+               0x00000030, 0x00000036, 0x000000a5, 0x00000038,
+               0x000000bf, 0x00000040, 0x000000a3, 0x0000009e,
+               0x00000081, 0x000000f3, 0x000000d7, 0x000000fb,
+               0x0000007c, 0x000000e3, 0x00000039, 0x00000082,
+               0x0000009b, 0x0000002f, 0x000000ff, 0x00000087,
+               0x00000034, 0x0000008e, 0x00000043, 0x00000044,
+               0x000000c4, 0x000000de, 0x000000e9, 0x000000cb,
+               0x00000054, 0x0000007b, 0x00000094, 0x00000032,
+               0x000000a6, 0x000000c2, 0x00000023, 0x0000003d,
+               0x000000ee, 0x0000004c, 0x00000095, 0x0000000b,
+               0x00000042, 0x000000fa, 0x000000c3, 0x0000004e,
+               0x00000008, 0x0000002e, 0x000000a1, 0x00000066,
+               0x00000028, 0x000000d9, 0x00000024, 0x000000b2,
+               0x00000076, 0x0000005b, 0x000000a2, 0x00000049,
+               0x0000006d, 0x0000008b, 0x000000d1, 0x00000025,
+               0x00000072, 0x000000f8, 0x000000f6, 0x00000064,
+               0x00000086, 0x00000068, 0x00000098, 0x00000016,
+               0x000000d4, 0x000000a4, 0x0000005c, 0x000000cc,
+               0x0000005d, 0x00000065, 0x000000b6, 0x00000092,
+               0x0000006c, 0x00000070, 0x00000048, 0x00000050,
+               0x000000fd, 0x000000ed, 0x000000b9, 0x000000da,
+               0x0000005e, 0x00000015, 0x00000046, 0x00000057,
+               0x000000a7, 0x0000008d, 0x0000009d, 0x00000084,
+               0x00000090, 0x000000d8, 0x000000ab, 0x00000000,
+               0x0000008c, 0x000000bc, 0x000000d3, 0x0000000a,
+               0x000000f7, 0x000000e4, 0x00000058, 0x00000005,
+               0x000000b8, 0x000000b3, 0x00000045, 0x00000006,
+               0x000000d0, 0x0000002c, 0x0000001e, 0x0000008f,
+               0x000000ca, 0x0000003f, 0x0000000f, 0x00000002,
+               0x000000c1, 0x000000af, 0x000000bd, 0x00000003,
+               0x00000001, 0x00000013, 0x0000008a, 0x0000006b,
+               0x0000003a, 0x00000091, 0x00000011, 0x00000041,
+               0x0000004f, 0x00000067, 0x000000dc, 0x000000ea,
+               0x00000097, 0x000000f2, 0x000000cf, 0x000000ce,
+               0x000000f0, 0x000000b4, 0x000000e6, 0x00000073,
+               0x00000096, 0x000000ac, 0x00000074, 0x00000022,
+               0x000000e7, 0x000000ad, 0x00000035, 0x00000085,
+               0x000000e2, 0x000000f9, 0x00000037, 0x000000e8,
+               0x0000001c, 0x00000075, 0x000000df, 0x0000006e,
+               0x00000047, 0x000000f1, 0x0000001a, 0x00000071,
+               0x0000001d, 0x00000029, 0x000000c5, 0x00000089,
+               0x0000006f, 0x000000b7, 0x00000062, 0x0000000e,
+               0x000000aa, 0x00000018, 0x000000be, 0x0000001b,
+               0x000000fc, 0x00000056, 0x0000003e, 0x0000004b,
+               0x000000c6, 0x000000d2, 0x00000079, 0x00000020,
+               0x0000009a, 0x000000db, 0x000000c0, 0x000000fe,
+               0x00000078, 0x000000cd, 0x0000005a, 0x000000f4,
+               0x0000001f, 0x000000dd, 0x000000a8, 0x00000033,
+               0x00000088, 0x00000007, 0x000000c7, 0x00000031,
+               0x000000b1, 0x00000012, 0x00000010, 0x00000059,
+               0x00000027, 0x00000080, 0x000000ec, 0x0000005f,
+               0x00000060, 0x00000051, 0x0000007f, 0x000000a9,
+               0x00000019, 0x000000b5, 0x0000004a, 0x0000000d,
+               0x0000002d, 0x000000e5, 0x0000007a, 0x0000009f,
+               0x00000093, 0x000000c9, 0x0000009c, 0x000000ef,
+               0x000000a0, 0x000000e0, 0x0000003b, 0x0000004d,
+               0x000000ae, 0x0000002a, 0x000000f5, 0x000000b0,
+               0x000000c8, 0x000000eb, 0x000000bb, 0x0000003c,
+               0x00000083, 0x00000053, 0x00000099, 0x00000061,
+               0x00000017, 0x0000002b, 0x00000004, 0x0000007e,
+               0x000000ba, 0x00000077, 0x000000d6, 0x00000026,
+               0x000000e1, 0x00000069, 0x00000014, 0x00000063,
+               0x00000055, 0x00000021, 0x0000000c, 0x0000007d,
+       }, {
+               0x00005200, 0x00000900, 0x00006a00, 0x0000d500,
+               0x00003000, 0x00003600, 0x0000a500, 0x00003800,
+               0x0000bf00, 0x00004000, 0x0000a300, 0x00009e00,
+               0x00008100, 0x0000f300, 0x0000d700, 0x0000fb00,
+               0x00007c00, 0x0000e300, 0x00003900, 0x00008200,
+               0x00009b00, 0x00002f00, 0x0000ff00, 0x00008700,
+               0x00003400, 0x00008e00, 0x00004300, 0x00004400,
+               0x0000c400, 0x0000de00, 0x0000e900, 0x0000cb00,
+               0x00005400, 0x00007b00, 0x00009400, 0x00003200,
+               0x0000a600, 0x0000c200, 0x00002300, 0x00003d00,
+               0x0000ee00, 0x00004c00, 0x00009500, 0x00000b00,
+               0x00004200, 0x0000fa00, 0x0000c300, 0x00004e00,
+               0x00000800, 0x00002e00, 0x0000a100, 0x00006600,
+               0x00002800, 0x0000d900, 0x00002400, 0x0000b200,
+               0x00007600, 0x00005b00, 0x0000a200, 0x00004900,
+               0x00006d00, 0x00008b00, 0x0000d100, 0x00002500,
+               0x00007200, 0x0000f800, 0x0000f600, 0x00006400,
+               0x00008600, 0x00006800, 0x00009800, 0x00001600,
+               0x0000d400, 0x0000a400, 0x00005c00, 0x0000cc00,
+               0x00005d00, 0x00006500, 0x0000b600, 0x00009200,
+               0x00006c00, 0x00007000, 0x00004800, 0x00005000,
+               0x0000fd00, 0x0000ed00, 0x0000b900, 0x0000da00,
+               0x00005e00, 0x00001500, 0x00004600, 0x00005700,
+               0x0000a700, 0x00008d00, 0x00009d00, 0x00008400,
+               0x00009000, 0x0000d800, 0x0000ab00, 0x00000000,
+               0x00008c00, 0x0000bc00, 0x0000d300, 0x00000a00,
+               0x0000f700, 0x0000e400, 0x00005800, 0x00000500,
+               0x0000b800, 0x0000b300, 0x00004500, 0x00000600,
+               0x0000d000, 0x00002c00, 0x00001e00, 0x00008f00,
+               0x0000ca00, 0x00003f00, 0x00000f00, 0x00000200,
+               0x0000c100, 0x0000af00, 0x0000bd00, 0x00000300,
+               0x00000100, 0x00001300, 0x00008a00, 0x00006b00,
+               0x00003a00, 0x00009100, 0x00001100, 0x00004100,
+               0x00004f00, 0x00006700, 0x0000dc00, 0x0000ea00,
+               0x00009700, 0x0000f200, 0x0000cf00, 0x0000ce00,
+               0x0000f000, 0x0000b400, 0x0000e600, 0x00007300,
+               0x00009600, 0x0000ac00, 0x00007400, 0x00002200,
+               0x0000e700, 0x0000ad00, 0x00003500, 0x00008500,
+               0x0000e200, 0x0000f900, 0x00003700, 0x0000e800,
+               0x00001c00, 0x00007500, 0x0000df00, 0x00006e00,
+               0x00004700, 0x0000f100, 0x00001a00, 0x00007100,
+               0x00001d00, 0x00002900, 0x0000c500, 0x00008900,
+               0x00006f00, 0x0000b700, 0x00006200, 0x00000e00,
+               0x0000aa00, 0x00001800, 0x0000be00, 0x00001b00,
+               0x0000fc00, 0x00005600, 0x00003e00, 0x00004b00,
+               0x0000c600, 0x0000d200, 0x00007900, 0x00002000,
+               0x00009a00, 0x0000db00, 0x0000c000, 0x0000fe00,
+               0x00007800, 0x0000cd00, 0x00005a00, 0x0000f400,
+               0x00001f00, 0x0000dd00, 0x0000a800, 0x00003300,
+               0x00008800, 0x00000700, 0x0000c700, 0x00003100,
+               0x0000b100, 0x00001200, 0x00001000, 0x00005900,
+               0x00002700, 0x00008000, 0x0000ec00, 0x00005f00,
+               0x00006000, 0x00005100, 0x00007f00, 0x0000a900,
+               0x00001900, 0x0000b500, 0x00004a00, 0x00000d00,
+               0x00002d00, 0x0000e500, 0x00007a00, 0x00009f00,
+               0x00009300, 0x0000c900, 0x00009c00, 0x0000ef00,
+               0x0000a000, 0x0000e000, 0x00003b00, 0x00004d00,
+               0x0000ae00, 0x00002a00, 0x0000f500, 0x0000b000,
+               0x0000c800, 0x0000eb00, 0x0000bb00, 0x00003c00,
+               0x00008300, 0x00005300, 0x00009900, 0x00006100,
+               0x00001700, 0x00002b00, 0x00000400, 0x00007e00,
+               0x0000ba00, 0x00007700, 0x0000d600, 0x00002600,
+               0x0000e100, 0x00006900, 0x00001400, 0x00006300,
+               0x00005500, 0x00002100, 0x00000c00, 0x00007d00,
+       }, {
+               0x00520000, 0x00090000, 0x006a0000, 0x00d50000,
+               0x00300000, 0x00360000, 0x00a50000, 0x00380000,
+               0x00bf0000, 0x00400000, 0x00a30000, 0x009e0000,
+               0x00810000, 0x00f30000, 0x00d70000, 0x00fb0000,
+               0x007c0000, 0x00e30000, 0x00390000, 0x00820000,
+               0x009b0000, 0x002f0000, 0x00ff0000, 0x00870000,
+               0x00340000, 0x008e0000, 0x00430000, 0x00440000,
+               0x00c40000, 0x00de0000, 0x00e90000, 0x00cb0000,
+               0x00540000, 0x007b0000, 0x00940000, 0x00320000,
+               0x00a60000, 0x00c20000, 0x00230000, 0x003d0000,
+               0x00ee0000, 0x004c0000, 0x00950000, 0x000b0000,
+               0x00420000, 0x00fa0000, 0x00c30000, 0x004e0000,
+               0x00080000, 0x002e0000, 0x00a10000, 0x00660000,
+               0x00280000, 0x00d90000, 0x00240000, 0x00b20000,
+               0x00760000, 0x005b0000, 0x00a20000, 0x00490000,
+               0x006d0000, 0x008b0000, 0x00d10000, 0x00250000,
+               0x00720000, 0x00f80000, 0x00f60000, 0x00640000,
+               0x00860000, 0x00680000, 0x00980000, 0x00160000,
+               0x00d40000, 0x00a40000, 0x005c0000, 0x00cc0000,
+               0x005d0000, 0x00650000, 0x00b60000, 0x00920000,
+               0x006c0000, 0x00700000, 0x00480000, 0x00500000,
+               0x00fd0000, 0x00ed0000, 0x00b90000, 0x00da0000,
+               0x005e0000, 0x00150000, 0x00460000, 0x00570000,
+               0x00a70000, 0x008d0000, 0x009d0000, 0x00840000,
+               0x00900000, 0x00d80000, 0x00ab0000, 0x00000000,
+               0x008c0000, 0x00bc0000, 0x00d30000, 0x000a0000,
+               0x00f70000, 0x00e40000, 0x00580000, 0x00050000,
+               0x00b80000, 0x00b30000, 0x00450000, 0x00060000,
+               0x00d00000, 0x002c0000, 0x001e0000, 0x008f0000,
+               0x00ca0000, 0x003f0000, 0x000f0000, 0x00020000,
+               0x00c10000, 0x00af0000, 0x00bd0000, 0x00030000,
+               0x00010000, 0x00130000, 0x008a0000, 0x006b0000,
+               0x003a0000, 0x00910000, 0x00110000, 0x00410000,
+               0x004f0000, 0x00670000, 0x00dc0000, 0x00ea0000,
+               0x00970000, 0x00f20000, 0x00cf0000, 0x00ce0000,
+               0x00f00000, 0x00b40000, 0x00e60000, 0x00730000,
+               0x00960000, 0x00ac0000, 0x00740000, 0x00220000,
+               0x00e70000, 0x00ad0000, 0x00350000, 0x00850000,
+               0x00e20000, 0x00f90000, 0x00370000, 0x00e80000,
+               0x001c0000, 0x00750000, 0x00df0000, 0x006e0000,
+               0x00470000, 0x00f10000, 0x001a0000, 0x00710000,
+               0x001d0000, 0x00290000, 0x00c50000, 0x00890000,
+               0x006f0000, 0x00b70000, 0x00620000, 0x000e0000,
+               0x00aa0000, 0x00180000, 0x00be0000, 0x001b0000,
+               0x00fc0000, 0x00560000, 0x003e0000, 0x004b0000,
+               0x00c60000, 0x00d20000, 0x00790000, 0x00200000,
+               0x009a0000, 0x00db0000, 0x00c00000, 0x00fe0000,
+               0x00780000, 0x00cd0000, 0x005a0000, 0x00f40000,
+               0x001f0000, 0x00dd0000, 0x00a80000, 0x00330000,
+               0x00880000, 0x00070000, 0x00c70000, 0x00310000,
+               0x00b10000, 0x00120000, 0x00100000, 0x00590000,
+               0x00270000, 0x00800000, 0x00ec0000, 0x005f0000,
+               0x00600000, 0x00510000, 0x007f0000, 0x00a90000,
+               0x00190000, 0x00b50000, 0x004a0000, 0x000d0000,
+               0x002d0000, 0x00e50000, 0x007a0000, 0x009f0000,
+               0x00930000, 0x00c90000, 0x009c0000, 0x00ef0000,
+               0x00a00000, 0x00e00000, 0x003b0000, 0x004d0000,
+               0x00ae0000, 0x002a0000, 0x00f50000, 0x00b00000,
+               0x00c80000, 0x00eb0000, 0x00bb0000, 0x003c0000,
+               0x00830000, 0x00530000, 0x00990000, 0x00610000,
+               0x00170000, 0x002b0000, 0x00040000, 0x007e0000,
+               0x00ba0000, 0x00770000, 0x00d60000, 0x00260000,
+               0x00e10000, 0x00690000, 0x00140000, 0x00630000,
+               0x00550000, 0x00210000, 0x000c0000, 0x007d0000,
+       }, {
+               0x52000000, 0x09000000, 0x6a000000, 0xd5000000,
+               0x30000000, 0x36000000, 0xa5000000, 0x38000000,
+               0xbf000000, 0x40000000, 0xa3000000, 0x9e000000,
+               0x81000000, 0xf3000000, 0xd7000000, 0xfb000000,
+               0x7c000000, 0xe3000000, 0x39000000, 0x82000000,
+               0x9b000000, 0x2f000000, 0xff000000, 0x87000000,
+               0x34000000, 0x8e000000, 0x43000000, 0x44000000,
+               0xc4000000, 0xde000000, 0xe9000000, 0xcb000000,
+               0x54000000, 0x7b000000, 0x94000000, 0x32000000,
+               0xa6000000, 0xc2000000, 0x23000000, 0x3d000000,
+               0xee000000, 0x4c000000, 0x95000000, 0x0b000000,
+               0x42000000, 0xfa000000, 0xc3000000, 0x4e000000,
+               0x08000000, 0x2e000000, 0xa1000000, 0x66000000,
+               0x28000000, 0xd9000000, 0x24000000, 0xb2000000,
+               0x76000000, 0x5b000000, 0xa2000000, 0x49000000,
+               0x6d000000, 0x8b000000, 0xd1000000, 0x25000000,
+               0x72000000, 0xf8000000, 0xf6000000, 0x64000000,
+               0x86000000, 0x68000000, 0x98000000, 0x16000000,
+               0xd4000000, 0xa4000000, 0x5c000000, 0xcc000000,
+               0x5d000000, 0x65000000, 0xb6000000, 0x92000000,
+               0x6c000000, 0x70000000, 0x48000000, 0x50000000,
+               0xfd000000, 0xed000000, 0xb9000000, 0xda000000,
+               0x5e000000, 0x15000000, 0x46000000, 0x57000000,
+               0xa7000000, 0x8d000000, 0x9d000000, 0x84000000,
+               0x90000000, 0xd8000000, 0xab000000, 0x00000000,
+               0x8c000000, 0xbc000000, 0xd3000000, 0x0a000000,
+               0xf7000000, 0xe4000000, 0x58000000, 0x05000000,
+               0xb8000000, 0xb3000000, 0x45000000, 0x06000000,
+               0xd0000000, 0x2c000000, 0x1e000000, 0x8f000000,
+               0xca000000, 0x3f000000, 0x0f000000, 0x02000000,
+               0xc1000000, 0xaf000000, 0xbd000000, 0x03000000,
+               0x01000000, 0x13000000, 0x8a000000, 0x6b000000,
+               0x3a000000, 0x91000000, 0x11000000, 0x41000000,
+               0x4f000000, 0x67000000, 0xdc000000, 0xea000000,
+               0x97000000, 0xf2000000, 0xcf000000, 0xce000000,
+               0xf0000000, 0xb4000000, 0xe6000000, 0x73000000,
+               0x96000000, 0xac000000, 0x74000000, 0x22000000,
+               0xe7000000, 0xad000000, 0x35000000, 0x85000000,
+               0xe2000000, 0xf9000000, 0x37000000, 0xe8000000,
+               0x1c000000, 0x75000000, 0xdf000000, 0x6e000000,
+               0x47000000, 0xf1000000, 0x1a000000, 0x71000000,
+               0x1d000000, 0x29000000, 0xc5000000, 0x89000000,
+               0x6f000000, 0xb7000000, 0x62000000, 0x0e000000,
+               0xaa000000, 0x18000000, 0xbe000000, 0x1b000000,
+               0xfc000000, 0x56000000, 0x3e000000, 0x4b000000,
+               0xc6000000, 0xd2000000, 0x79000000, 0x20000000,
+               0x9a000000, 0xdb000000, 0xc0000000, 0xfe000000,
+               0x78000000, 0xcd000000, 0x5a000000, 0xf4000000,
+               0x1f000000, 0xdd000000, 0xa8000000, 0x33000000,
+               0x88000000, 0x07000000, 0xc7000000, 0x31000000,
+               0xb1000000, 0x12000000, 0x10000000, 0x59000000,
+               0x27000000, 0x80000000, 0xec000000, 0x5f000000,
+               0x60000000, 0x51000000, 0x7f000000, 0xa9000000,
+               0x19000000, 0xb5000000, 0x4a000000, 0x0d000000,
+               0x2d000000, 0xe5000000, 0x7a000000, 0x9f000000,
+               0x93000000, 0xc9000000, 0x9c000000, 0xef000000,
+               0xa0000000, 0xe0000000, 0x3b000000, 0x4d000000,
+               0xae000000, 0x2a000000, 0xf5000000, 0xb0000000,
+               0xc8000000, 0xeb000000, 0xbb000000, 0x3c000000,
+               0x83000000, 0x53000000, 0x99000000, 0x61000000,
+               0x17000000, 0x2b000000, 0x04000000, 0x7e000000,
+               0xba000000, 0x77000000, 0xd6000000, 0x26000000,
+               0xe1000000, 0x69000000, 0x14000000, 0x63000000,
+               0x55000000, 0x21000000, 0x0c000000, 0x7d000000,
        }
-}
+};
+
+EXPORT_SYMBOL_GPL(crypto_ft_tab);
+EXPORT_SYMBOL_GPL(crypto_fl_tab);
+EXPORT_SYMBOL_GPL(crypto_it_tab);
+EXPORT_SYMBOL_GPL(crypto_il_tab);
 
 /* initialise the key schedule from the user supplied key */
 
@@ -491,7 +1457,6 @@ static struct crypto_alg aes_alg = {
 
 static int __init aes_init(void)
 {
-       gen_tabs();
        return crypto_register_alg(&aes_alg);
 }
 
index 27128f2..ba5292d 100644 (file)
@@ -112,6 +112,22 @@ int crypto_hash_walk_first(struct ahash_request *req,
 }
 EXPORT_SYMBOL_GPL(crypto_hash_walk_first);
 
+int crypto_hash_walk_first_compat(struct hash_desc *hdesc,
+                                 struct crypto_hash_walk *walk,
+                                 struct scatterlist *sg, unsigned int len)
+{
+       walk->total = len;
+
+       if (!walk->total)
+               return 0;
+
+       walk->alignmask = crypto_hash_alignmask(hdesc->tfm);
+       walk->sg = sg;
+       walk->flags = hdesc->flags;
+
+       return hash_walk_new_entry(walk);
+}
+
 static int ahash_setkey_unaligned(struct crypto_ahash *tfm, const u8 *key,
                                unsigned int keylen)
 {
@@ -146,6 +162,26 @@ static int ahash_setkey(struct crypto_ahash *tfm, const u8 *key,
        return ahash->setkey(tfm, key, keylen);
 }
 
+static int ahash_nosetkey(struct crypto_ahash *tfm, const u8 *key,
+                         unsigned int keylen)
+{
+       return -ENOSYS;
+}
+
+int crypto_ahash_import(struct ahash_request *req, const u8 *in)
+{
+       struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+       struct ahash_alg *alg = crypto_ahash_alg(tfm);
+
+       memcpy(ahash_request_ctx(req), in, crypto_ahash_reqsize(tfm));
+
+       if (alg->reinit)
+               alg->reinit(req);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(crypto_ahash_import);
+
 static unsigned int crypto_ahash_ctxsize(struct crypto_alg *alg, u32 type,
                                        u32 mask)
 {
@@ -164,7 +200,7 @@ static int crypto_init_ahash_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
        crt->update = alg->update;
        crt->final  = alg->final;
        crt->digest = alg->digest;
-       crt->setkey = ahash_setkey;
+       crt->setkey = alg->setkey ? ahash_setkey : ahash_nosetkey;
        crt->digestsize = alg->digestsize;
 
        return 0;
index 72db0fd..0fac8ff 100644 (file)
@@ -161,7 +161,7 @@ static int _get_more_prng_bytes(struct prng_context *ctx)
        /*
         * Now update our DT value
         */
-       for (i = 0; i < DEFAULT_BLK_SZ; i++) {
+       for (i = DEFAULT_BLK_SZ - 1; i >= 0; i--) {
                ctx->DT[i] += 1;
                if (ctx->DT[i] != 0)
                        break;
@@ -223,9 +223,10 @@ remainder:
        }
 
        /*
-        * Copy up to the next whole block size
+        * Copy any data less than an entire block
         */
        if (byte_count < DEFAULT_BLK_SZ) {
+empty_rbuf:
                for (; ctx->rand_data_valid < DEFAULT_BLK_SZ;
                        ctx->rand_data_valid++) {
                        *ptr = ctx->rand_data[ctx->rand_data_valid];
@@ -240,18 +241,22 @@ remainder:
         * Now copy whole blocks
         */
        for (; byte_count >= DEFAULT_BLK_SZ; byte_count -= DEFAULT_BLK_SZ) {
-               if (_get_more_prng_bytes(ctx) < 0) {
-                       memset(buf, 0, nbytes);
-                       err = -EINVAL;
-                       goto done;
+               if (ctx->rand_data_valid == DEFAULT_BLK_SZ) {
+                       if (_get_more_prng_bytes(ctx) < 0) {
+                               memset(buf, 0, nbytes);
+                               err = -EINVAL;
+                               goto done;
+                       }
                }
+               if (ctx->rand_data_valid > 0)
+                       goto empty_rbuf;
                memcpy(ptr, ctx->rand_data, DEFAULT_BLK_SZ);
                ctx->rand_data_valid += DEFAULT_BLK_SZ;
                ptr += DEFAULT_BLK_SZ;
        }
 
        /*
-        * Now copy any extra partial data
+        * Now go back and get any remaining partial block
         */
        if (byte_count)
                goto remainder;
@@ -349,15 +354,25 @@ static int cprng_get_random(struct crypto_rng *tfm, u8 *rdata,
        return get_prng_bytes(rdata, dlen, prng);
 }
 
+/*
+ *  This is the cprng_registered reset method the seed value is
+ *  interpreted as the tuple { V KEY DT}
+ *  V and KEY are required during reset, and DT is optional, detected
+ *  as being present by testing the length of the seed
+ */
 static int cprng_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
 {
        struct prng_context *prng = crypto_rng_ctx(tfm);
-       u8 *key = seed + DEFAULT_PRNG_KSZ;
+       u8 *key = seed + DEFAULT_BLK_SZ;
+       u8 *dt = NULL;
 
        if (slen < DEFAULT_PRNG_KSZ + DEFAULT_BLK_SZ)
                return -EINVAL;
 
-       reset_prng_context(prng, key, DEFAULT_PRNG_KSZ, seed, NULL);
+       if (slen >= (2 * DEFAULT_BLK_SZ + DEFAULT_PRNG_KSZ))
+               dt = key + DEFAULT_PRNG_KSZ;
+
+       reset_prng_context(prng, key, DEFAULT_PRNG_KSZ, seed, dt);
 
        if (prng->flags & PRNG_NEED_RESET)
                return -EINVAL;
@@ -379,7 +394,7 @@ static struct crypto_alg rng_alg = {
                .rng = {
                        .rng_make_random        = cprng_get_random,
                        .rng_reset              = cprng_reset,
-                       .seedsize = DEFAULT_PRNG_KSZ + DEFAULT_BLK_SZ,
+                       .seedsize = DEFAULT_PRNG_KSZ + 2*DEFAULT_BLK_SZ,
                }
        }
 };
index 0444d24..9975a7b 100644 (file)
@@ -300,8 +300,8 @@ static void crypto_exit_ops(struct crypto_tfm *tfm)
        const struct crypto_type *type = tfm->__crt_alg->cra_type;
 
        if (type) {
-               if (type->exit)
-                       type->exit(tfm);
+               if (tfm->exit)
+                       tfm->exit(tfm);
                return;
        }
 
@@ -379,17 +379,16 @@ struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type,
        if (err)
                goto out_free_tfm;
 
-       if (alg->cra_init && (err = alg->cra_init(tfm))) {
-               if (err == -EAGAIN)
-                       crypto_shoot_alg(alg);
+       if (!tfm->exit && alg->cra_init && (err = alg->cra_init(tfm)))
                goto cra_init_failed;
-       }
 
        goto out;
 
 cra_init_failed:
        crypto_exit_ops(tfm);
 out_free_tfm:
+       if (err == -EAGAIN)
+               crypto_shoot_alg(alg);
        kfree(tfm);
 out_err:
        tfm = ERR_PTR(err);
@@ -404,6 +403,9 @@ EXPORT_SYMBOL_GPL(__crypto_alloc_tfm);
  *     @type: Type of algorithm
  *     @mask: Mask for type comparison
  *
+ *     This function should not be used by new algorithm types.
+ *     Plesae use crypto_alloc_tfm instead.
+ *
  *     crypto_alloc_base() will first attempt to locate an already loaded
  *     algorithm.  If that fails and the kernel supports dynamically loadable
  *     modules, it will then attempt to load a module of the same name or
@@ -450,6 +452,111 @@ err:
        return ERR_PTR(err);
 }
 EXPORT_SYMBOL_GPL(crypto_alloc_base);
+
+struct crypto_tfm *crypto_create_tfm(struct crypto_alg *alg,
+                                    const struct crypto_type *frontend)
+{
+       char *mem;
+       struct crypto_tfm *tfm = NULL;
+       unsigned int tfmsize;
+       unsigned int total;
+       int err = -ENOMEM;
+
+       tfmsize = frontend->tfmsize;
+       total = tfmsize + sizeof(*tfm) + frontend->extsize(alg, frontend);
+
+       mem = kzalloc(total, GFP_KERNEL);
+       if (mem == NULL)
+               goto out_err;
+
+       tfm = (struct crypto_tfm *)(mem + tfmsize);
+       tfm->__crt_alg = alg;
+
+       err = frontend->init_tfm(tfm, frontend);
+       if (err)
+               goto out_free_tfm;
+
+       if (!tfm->exit && alg->cra_init && (err = alg->cra_init(tfm)))
+               goto cra_init_failed;
+
+       goto out;
+
+cra_init_failed:
+       crypto_exit_ops(tfm);
+out_free_tfm:
+       if (err == -EAGAIN)
+               crypto_shoot_alg(alg);
+       kfree(mem);
+out_err:
+       tfm = ERR_PTR(err);
+out:
+       return tfm;
+}
+EXPORT_SYMBOL_GPL(crypto_create_tfm);
+
+/*
+ *     crypto_alloc_tfm - Locate algorithm and allocate transform
+ *     @alg_name: Name of algorithm
+ *     @frontend: Frontend algorithm type
+ *     @type: Type of algorithm
+ *     @mask: Mask for type comparison
+ *
+ *     crypto_alloc_tfm() will first attempt to locate an already loaded
+ *     algorithm.  If that fails and the kernel supports dynamically loadable
+ *     modules, it will then attempt to load a module of the same name or
+ *     alias.  If that fails it will send a query to any loaded crypto manager
+ *     to construct an algorithm on the fly.  A refcount is grabbed on the
+ *     algorithm which is then associated with the new transform.
+ *
+ *     The returned transform is of a non-determinate type.  Most people
+ *     should use one of the more specific allocation functions such as
+ *     crypto_alloc_blkcipher.
+ *
+ *     In case of error the return value is an error pointer.
+ */
+struct crypto_tfm *crypto_alloc_tfm(const char *alg_name,
+                                   const struct crypto_type *frontend,
+                                   u32 type, u32 mask)
+{
+       struct crypto_alg *(*lookup)(const char *name, u32 type, u32 mask);
+       struct crypto_tfm *tfm;
+       int err;
+
+       type &= frontend->maskclear;
+       mask &= frontend->maskclear;
+       type |= frontend->type;
+       mask |= frontend->maskset;
+
+       lookup = frontend->lookup ?: crypto_alg_mod_lookup;
+
+       for (;;) {
+               struct crypto_alg *alg;
+
+               alg = lookup(alg_name, type, mask);
+               if (IS_ERR(alg)) {
+                       err = PTR_ERR(alg);
+                       goto err;
+               }
+
+               tfm = crypto_create_tfm(alg, frontend);
+               if (!IS_ERR(tfm))
+                       return tfm;
+
+               crypto_mod_put(alg);
+               err = PTR_ERR(tfm);
+
+err:
+               if (err != -EAGAIN)
+                       break;
+               if (signal_pending(current)) {
+                       err = -EINTR;
+                       break;
+               }
+       }
+
+       return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(crypto_alloc_tfm);
  
 /*
  *     crypto_free_tfm - Free crypto transform
@@ -469,7 +576,7 @@ void crypto_free_tfm(struct crypto_tfm *tfm)
        alg = tfm->__crt_alg;
        size = sizeof(*tfm) + alg->cra_ctxsize;
 
-       if (alg->cra_exit)
+       if (!tfm->exit && alg->cra_exit)
                alg->cra_exit(tfm);
        crypto_exit_ops(tfm);
        crypto_mod_put(alg);
index fd9f06c..40b6e9e 100644 (file)
@@ -11,6 +11,7 @@
  */
 
 #include <crypto/aead.h>
+#include <crypto/internal/hash.h>
 #include <crypto/internal/skcipher.h>
 #include <crypto/authenc.h>
 #include <crypto/scatterwalk.h>
@@ -431,6 +432,8 @@ static struct crypto_instance *crypto_authenc_alloc(struct rtattr **tb)
        inst->alg.cra_aead.ivsize = enc->cra_ablkcipher.ivsize;
        inst->alg.cra_aead.maxauthsize = auth->cra_type == &crypto_hash_type ?
                                         auth->cra_hash.digestsize :
+                                        auth->cra_type ?
+                                        __crypto_shash_alg(auth)->digestsize :
                                         auth->cra_digest.dia_digestsize;
 
        inst->alg.cra_ctxsize = sizeof(struct crypto_authenc_ctx);
index 493fee7..964635d 100644 (file)
@@ -35,6 +35,8 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/bitops.h>
+#include <asm/unaligned.h>
 
 static const u32 camellia_sp1110[256] = {
        0x70707000,0x82828200,0x2c2c2c00,0xececec00,
@@ -335,20 +337,6 @@ static const u32 camellia_sp4404[256] = {
 /*
  *  macros
  */
-#define GETU32(v, pt) \
-    do { \
-       /* latest breed of gcc is clever enough to use move */ \
-       memcpy(&(v), (pt), 4); \
-       (v) = be32_to_cpu(v); \
-    } while(0)
-
-/* rotation right shift 1byte */
-#define ROR8(x) (((x) >> 8) + ((x) << 24))
-/* rotation left shift 1bit */
-#define ROL1(x) (((x) << 1) + ((x) >> 31))
-/* rotation left shift 1byte */
-#define ROL8(x) (((x) << 8) + ((x) >> 24))
-
 #define ROLDQ(ll, lr, rl, rr, w0, w1, bits)            \
     do {                                               \
        w0 = ll;                                        \
@@ -383,7 +371,7 @@ static const u32 camellia_sp4404[256] = {
           ^ camellia_sp3033[(u8)(il >> 8)]                     \
           ^ camellia_sp4404[(u8)(il     )];                    \
        yl ^= yr;                                               \
-       yr = ROR8(yr);                                          \
+       yr = ror32(yr, 8);                                      \
        yr ^= yl;                                               \
     } while(0)
 
@@ -405,7 +393,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
        subL[7] ^= subL[1]; subR[7] ^= subR[1];
        subL[1] ^= subR[1] & ~subR[9];
        dw = subL[1] & subL[9],
-               subR[1] ^= ROL1(dw); /* modified for FLinv(kl2) */
+               subR[1] ^= rol32(dw, 1); /* modified for FLinv(kl2) */
        /* round 8 */
        subL[11] ^= subL[1]; subR[11] ^= subR[1];
        /* round 10 */
@@ -414,7 +402,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
        subL[15] ^= subL[1]; subR[15] ^= subR[1];
        subL[1] ^= subR[1] & ~subR[17];
        dw = subL[1] & subL[17],
-               subR[1] ^= ROL1(dw); /* modified for FLinv(kl4) */
+               subR[1] ^= rol32(dw, 1); /* modified for FLinv(kl4) */
        /* round 14 */
        subL[19] ^= subL[1]; subR[19] ^= subR[1];
        /* round 16 */
@@ -430,7 +418,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
        } else {
                subL[1] ^= subR[1] & ~subR[25];
                dw = subL[1] & subL[25],
-                       subR[1] ^= ROL1(dw); /* modified for FLinv(kl6) */
+                       subR[1] ^= rol32(dw, 1); /* modified for FLinv(kl6) */
                /* round 20 */
                subL[27] ^= subL[1]; subR[27] ^= subR[1];
                /* round 22 */
@@ -450,7 +438,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
                subL[26] ^= kw4l; subR[26] ^= kw4r;
                kw4l ^= kw4r & ~subR[24];
                dw = kw4l & subL[24],
-                       kw4r ^= ROL1(dw); /* modified for FL(kl5) */
+                       kw4r ^= rol32(dw, 1); /* modified for FL(kl5) */
        }
        /* round 17 */
        subL[22] ^= kw4l; subR[22] ^= kw4r;
@@ -460,7 +448,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
        subL[18] ^= kw4l; subR[18] ^= kw4r;
        kw4l ^= kw4r & ~subR[16];
        dw = kw4l & subL[16],
-               kw4r ^= ROL1(dw); /* modified for FL(kl3) */
+               kw4r ^= rol32(dw, 1); /* modified for FL(kl3) */
        /* round 11 */
        subL[14] ^= kw4l; subR[14] ^= kw4r;
        /* round 9 */
@@ -469,7 +457,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
        subL[10] ^= kw4l; subR[10] ^= kw4r;
        kw4l ^= kw4r & ~subR[8];
        dw = kw4l & subL[8],
-               kw4r ^= ROL1(dw); /* modified for FL(kl1) */
+               kw4r ^= rol32(dw, 1); /* modified for FL(kl1) */
        /* round 5 */
        subL[6] ^= kw4l; subR[6] ^= kw4r;
        /* round 3 */
@@ -494,7 +482,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
        SUBKEY_R(6) = subR[5] ^ subR[7];
        tl = subL[10] ^ (subR[10] & ~subR[8]);
        dw = tl & subL[8],  /* FL(kl1) */
-               tr = subR[10] ^ ROL1(dw);
+               tr = subR[10] ^ rol32(dw, 1);
        SUBKEY_L(7) = subL[6] ^ tl; /* round 6 */
        SUBKEY_R(7) = subR[6] ^ tr;
        SUBKEY_L(8) = subL[8];       /* FL(kl1) */
@@ -503,7 +491,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
        SUBKEY_R(9) = subR[9];
        tl = subL[7] ^ (subR[7] & ~subR[9]);
        dw = tl & subL[9],  /* FLinv(kl2) */
-               tr = subR[7] ^ ROL1(dw);
+               tr = subR[7] ^ rol32(dw, 1);
        SUBKEY_L(10) = tl ^ subL[11]; /* round 7 */
        SUBKEY_R(10) = tr ^ subR[11];
        SUBKEY_L(11) = subL[10] ^ subL[12]; /* round 8 */
@@ -516,7 +504,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
        SUBKEY_R(14) = subR[13] ^ subR[15];
        tl = subL[18] ^ (subR[18] & ~subR[16]);
        dw = tl & subL[16], /* FL(kl3) */
-               tr = subR[18] ^ ROL1(dw);
+               tr = subR[18] ^ rol32(dw, 1);
        SUBKEY_L(15) = subL[14] ^ tl; /* round 12 */
        SUBKEY_R(15) = subR[14] ^ tr;
        SUBKEY_L(16) = subL[16];     /* FL(kl3) */
@@ -525,7 +513,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
        SUBKEY_R(17) = subR[17];
        tl = subL[15] ^ (subR[15] & ~subR[17]);
        dw = tl & subL[17], /* FLinv(kl4) */
-               tr = subR[15] ^ ROL1(dw);
+               tr = subR[15] ^ rol32(dw, 1);
        SUBKEY_L(18) = tl ^ subL[19]; /* round 13 */
        SUBKEY_R(18) = tr ^ subR[19];
        SUBKEY_L(19) = subL[18] ^ subL[20]; /* round 14 */
@@ -544,7 +532,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
        } else {
                tl = subL[26] ^ (subR[26] & ~subR[24]);
                dw = tl & subL[24], /* FL(kl5) */
-                       tr = subR[26] ^ ROL1(dw);
+                       tr = subR[26] ^ rol32(dw, 1);
                SUBKEY_L(23) = subL[22] ^ tl; /* round 18 */
                SUBKEY_R(23) = subR[22] ^ tr;
                SUBKEY_L(24) = subL[24];     /* FL(kl5) */
@@ -553,7 +541,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
                SUBKEY_R(25) = subR[25];
                tl = subL[23] ^ (subR[23] & ~subR[25]);
                dw = tl & subL[25], /* FLinv(kl6) */
-                       tr = subR[23] ^ ROL1(dw);
+                       tr = subR[23] ^ rol32(dw, 1);
                SUBKEY_L(26) = tl ^ subL[27]; /* round 19 */
                SUBKEY_R(26) = tr ^ subR[27];
                SUBKEY_L(27) = subL[26] ^ subL[28]; /* round 20 */
@@ -573,17 +561,17 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
        /* apply the inverse of the last half of P-function */
        i = 2;
        do {
-               dw = SUBKEY_L(i + 0) ^ SUBKEY_R(i + 0); dw = ROL8(dw);/* round 1 */
+               dw = SUBKEY_L(i + 0) ^ SUBKEY_R(i + 0); dw = rol32(dw, 8);/* round 1 */
                SUBKEY_R(i + 0) = SUBKEY_L(i + 0) ^ dw; SUBKEY_L(i + 0) = dw;
-               dw = SUBKEY_L(i + 1) ^ SUBKEY_R(i + 1); dw = ROL8(dw);/* round 2 */
+               dw = SUBKEY_L(i + 1) ^ SUBKEY_R(i + 1); dw = rol32(dw, 8);/* round 2 */
                SUBKEY_R(i + 1) = SUBKEY_L(i + 1) ^ dw; SUBKEY_L(i + 1) = dw;
-               dw = SUBKEY_L(i + 2) ^ SUBKEY_R(i + 2); dw = ROL8(dw);/* round 3 */
+               dw = SUBKEY_L(i + 2) ^ SUBKEY_R(i + 2); dw = rol32(dw, 8);/* round 3 */
                SUBKEY_R(i + 2) = SUBKEY_L(i + 2) ^ dw; SUBKEY_L(i + 2) = dw;
-               dw = SUBKEY_L(i + 3) ^ SUBKEY_R(i + 3); dw = ROL8(dw);/* round 4 */
+               dw = SUBKEY_L(i + 3) ^ SUBKEY_R(i + 3); dw = rol32(dw, 8);/* round 4 */
                SUBKEY_R(i + 3) = SUBKEY_L(i + 3) ^ dw; SUBKEY_L(i + 3) = dw;
-               dw = SUBKEY_L(i + 4) ^ SUBKEY_R(i + 4); dw = ROL8(dw);/* round 5 */
+               dw = SUBKEY_L(i + 4) ^ SUBKEY_R(i + 4); dw = rol32(dw, 8);/* round 5 */
                SUBKEY_R(i + 4) = SUBKEY_L(i + 4) ^ dw; SUBKEY_L(i + 4) = dw;
-               dw = SUBKEY_L(i + 5) ^ SUBKEY_R(i + 5); dw = ROL8(dw);/* round 6 */
+               dw = SUBKEY_L(i + 5) ^ SUBKEY_R(i + 5); dw = rol32(dw, 8);/* round 6 */
                SUBKEY_R(i + 5) = SUBKEY_L(i + 5) ^ dw; SUBKEY_L(i + 5) = dw;
                i += 8;
        } while (i < max);
@@ -599,10 +587,10 @@ static void camellia_setup128(const unsigned char *key, u32 *subkey)
        /**
         *  k == kll || klr || krl || krr (|| is concatenation)
         */
-       GETU32(kll, key     );
-       GETU32(klr, key +  4);
-       GETU32(krl, key +  8);
-       GETU32(krr, key + 12);
+       kll = get_unaligned_be32(key);
+       klr = get_unaligned_be32(key + 4);
+       krl = get_unaligned_be32(key + 8);
+       krr = get_unaligned_be32(key + 12);
 
        /* generate KL dependent subkeys */
        /* kw1 */
@@ -707,14 +695,14 @@ static void camellia_setup256(const unsigned char *key, u32 *subkey)
         *  key = (kll || klr || krl || krr || krll || krlr || krrl || krrr)
         *  (|| is concatenation)
         */
-       GETU32(kll,  key     );
-       GETU32(klr,  key +  4);
-       GETU32(krl,  key +  8);
-       GETU32(krr,  key + 12);
-       GETU32(krll, key + 16);
-       GETU32(krlr, key + 20);
-       GETU32(krrl, key + 24);
-       GETU32(krrr, key + 28);
+       kll = get_unaligned_be32(key);
+       klr = get_unaligned_be32(key + 4);
+       krl = get_unaligned_be32(key + 8);
+       krr = get_unaligned_be32(key + 12);
+       krll = get_unaligned_be32(key + 16);
+       krlr = get_unaligned_be32(key + 20);
+       krrl = get_unaligned_be32(key + 24);
+       krrr = get_unaligned_be32(key + 28);
 
        /* generate KL dependent subkeys */
        /* kw1 */
@@ -870,13 +858,13 @@ static void camellia_setup192(const unsigned char *key, u32 *subkey)
        t0 &= ll;                                                       \
        t2 |= rr;                                                       \
        rl ^= t2;                                                       \
-       lr ^= ROL1(t0);                                                 \
+       lr ^= rol32(t0, 1);                                             \
        t3 = krl;                                                       \
        t1 = klr;                                                       \
        t3 &= rl;                                                       \
        t1 |= lr;                                                       \
        ll ^= t1;                                                       \
-       rr ^= ROL1(t3);                                                 \
+       rr ^= rol32(t3, 1);                                             \
     } while(0)
 
 #define CAMELLIA_ROUNDSM(xl, xr, kl, kr, yl, yr, il, ir)               \
@@ -892,7 +880,7 @@ static void camellia_setup192(const unsigned char *key, u32 *subkey)
        il ^= kl;                                                       \
        ir ^= il ^ kr;                                                  \
        yl ^= ir;                                                       \
-       yr ^= ROR8(il) ^ ir;                                            \
+       yr ^= ror32(il, 8) ^ ir;                                                \
     } while(0)
 
 /* max = 24: 128bit encrypt, max = 32: 256bit encrypt */
index a882d9e..973bc2c 100644 (file)
@@ -3,8 +3,29 @@
  *
  * CRC32C chksum
  *
- * This module file is a wrapper to invoke the lib/crc32c routines.
+ *@Article{castagnoli-crc,
+ * author =       { Guy Castagnoli and Stefan Braeuer and Martin Herrman},
+ * title =        {{Optimization of Cyclic Redundancy-Check Codes with 24
+ *                 and 32 Parity Bits}},
+ * journal =      IEEE Transactions on Communication,
+ * year =         {1993},
+ * volume =       {41},
+ * number =       {6},
+ * pages =        {},
+ * month =        {June},
+ *}
+ * Used by the iSCSI driver, possibly others, and derived from the
+ * the iscsi-crc.c module of the linux-iscsi driver at
+ * http://linux-iscsi.sourceforge.net.
  *
+ * Following the example of lib/crc32, this function is intended to be
+ * flexible and useful for all users.  Modules that currently have their
+ * own crc32c, but hopefully may be able to use this one are:
+ *  net/sctp (please add all your doco to here if you change to
+ *            use this one!)
+ *  <endoflist>
+ *
+ * Copyright (c) 2004 Cisco Systems, Inc.
  * Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au>
  *
  * This program is free software; you can redistribute it and/or modify it
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/string.h>
-#include <linux/crc32c.h>
 #include <linux/kernel.h>
 
 #define CHKSUM_BLOCK_SIZE      1
 #define CHKSUM_DIGEST_SIZE     4
 
 struct chksum_ctx {
-       u32 crc;
        u32 key;
 };
 
+struct chksum_desc_ctx {
+       u32 crc;
+};
+
 /*
- * Steps through buffer one byte at at time, calculates reflected 
- * crc using table.
+ * This is the CRC-32C table
+ * Generated with:
+ * width = 32 bits
+ * poly = 0x1EDC6F41
+ * reflect input bytes = true
+ * reflect output bytes = true
  */
 
-static void chksum_init(struct crypto_tfm *tfm)
-{
-       struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
-
-       mctx->crc = mctx->key;
-}
+static const u32 crc32c_table[256] = {
+       0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
+       0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
+       0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
+       0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
+       0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
+       0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
+       0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
+       0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
+       0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
+       0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
+       0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
+       0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
+       0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
+       0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
+       0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
+       0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
+       0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
+       0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
+       0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
+       0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
+       0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
+       0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
+       0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
+       0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
+       0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
+       0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
+       0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
+       0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
+       0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
+       0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
+       0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
+       0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
+       0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
+       0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
+       0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
+       0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
+       0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
+       0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
+       0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
+       0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
+       0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
+       0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
+       0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
+       0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
+       0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
+       0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
+       0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
+       0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
+       0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
+       0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
+       0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
+       0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
+       0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
+       0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
+       0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
+       0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
+       0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
+       0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
+       0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
+       0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
+       0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
+       0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
+       0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
+       0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
+};
 
 /*
- * Setting the seed allows arbitrary accumulators and flexible XOR policy
- * If your algorithm starts with ~0, then XOR with ~0 before you set
- * the seed.
+ * Steps through buffer one byte at at time, calculates reflected
+ * crc using table.
  */
-static int chksum_setkey(struct crypto_tfm *tfm, const u8 *key,
-                        unsigned int keylen)
-{
-       struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
 
-       if (keylen != sizeof(mctx->crc)) {
-               tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
-               return -EINVAL;
-       }
-       mctx->key = le32_to_cpu(*(__le32 *)key);
-       return 0;
-}
-
-static void chksum_update(struct crypto_tfm *tfm, const u8 *data,
-                         unsigned int length)
+static u32 crc32c(u32 crc, const u8 *data, unsigned int length)
 {
-       struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
+       while (length--)
+               crc = crc32c_table[(crc ^ *data++) & 0xFFL] ^ (crc >> 8);
 
-       mctx->crc = crc32c(mctx->crc, data, length);
+       return crc;
 }
 
-static void chksum_final(struct crypto_tfm *tfm, u8 *out)
-{
-       struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
-       
-       *(__le32 *)out = ~cpu_to_le32(mctx->crc);
-}
+/*
+ * Steps through buffer one byte at at time, calculates reflected 
+ * crc using table.
+ */
 
-static int crc32c_cra_init_old(struct crypto_tfm *tfm)
+static int chksum_init(struct shash_desc *desc)
 {
-       struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
+       struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm);
+       struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
+
+       ctx->crc = mctx->key;
 
-       mctx->key = ~0;
        return 0;
 }
 
-static struct crypto_alg old_alg = {
-       .cra_name       =       "crc32c",
-       .cra_flags      =       CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize  =       CHKSUM_BLOCK_SIZE,
-       .cra_ctxsize    =       sizeof(struct chksum_ctx),
-       .cra_module     =       THIS_MODULE,
-       .cra_list       =       LIST_HEAD_INIT(old_alg.cra_list),
-       .cra_init       =       crc32c_cra_init_old,
-       .cra_u          =       {
-               .digest = {
-                        .dia_digestsize=       CHKSUM_DIGEST_SIZE,
-                        .dia_setkey    =       chksum_setkey,
-                        .dia_init      =       chksum_init,
-                        .dia_update    =       chksum_update,
-                        .dia_final     =       chksum_final
-                }
-       }
-};
-
 /*
  * Setting the seed allows arbitrary accumulators and flexible XOR policy
  * If your algorithm starts with ~0, then XOR with ~0 before you set
  * the seed.
  */
-static int crc32c_setkey(struct crypto_ahash *hash, const u8 *key,
+static int chksum_setkey(struct crypto_shash *tfm, const u8 *key,
                         unsigned int keylen)
 {
-       u32 *mctx = crypto_ahash_ctx(hash);
+       struct chksum_ctx *mctx = crypto_shash_ctx(tfm);
 
-       if (keylen != sizeof(u32)) {
-               crypto_ahash_set_flags(hash, CRYPTO_TFM_RES_BAD_KEY_LEN);
+       if (keylen != sizeof(mctx->key)) {
+               crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
                return -EINVAL;
        }
-       *mctx = le32_to_cpup((__le32 *)key);
+       mctx->key = le32_to_cpu(*(__le32 *)key);
        return 0;
 }
 
-static int crc32c_init(struct ahash_request *req)
+static int chksum_update(struct shash_desc *desc, const u8 *data,
+                        unsigned int length)
 {
-       u32 *mctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
-       u32 *crcp = ahash_request_ctx(req);
+       struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
 
-       *crcp = *mctx;
+       ctx->crc = crc32c(ctx->crc, data, length);
        return 0;
 }
 
-static int crc32c_update(struct ahash_request *req)
+static int chksum_final(struct shash_desc *desc, u8 *out)
 {
-       struct crypto_hash_walk walk;
-       u32 *crcp = ahash_request_ctx(req);
-       u32 crc = *crcp;
-       int nbytes;
-
-       for (nbytes = crypto_hash_walk_first(req, &walk); nbytes;
-            nbytes = crypto_hash_walk_done(&walk, 0))
-               crc = crc32c(crc, walk.data, nbytes);
+       struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
 
-       *crcp = crc;
+       *(__le32 *)out = ~cpu_to_le32p(&ctx->crc);
        return 0;
 }
 
-static int crc32c_final(struct ahash_request *req)
+static int __chksum_finup(u32 *crcp, const u8 *data, unsigned int len, u8 *out)
 {
-       u32 *crcp = ahash_request_ctx(req);
-       
-       *(__le32 *)req->result = ~cpu_to_le32p(crcp);
+       *(__le32 *)out = ~cpu_to_le32(crc32c(*crcp, data, len));
        return 0;
 }
 
-static int crc32c_digest(struct ahash_request *req)
+static int chksum_finup(struct shash_desc *desc, const u8 *data,
+                       unsigned int len, u8 *out)
 {
-       struct crypto_hash_walk walk;
-       u32 *mctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
-       u32 crc = *mctx;
-       int nbytes;
+       struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
 
-       for (nbytes = crypto_hash_walk_first(req, &walk); nbytes;
-            nbytes = crypto_hash_walk_done(&walk, 0))
-               crc = crc32c(crc, walk.data, nbytes);
-
-       *(__le32 *)req->result = ~cpu_to_le32(crc);
-       return 0;
+       return __chksum_finup(&ctx->crc, data, len, out);
 }
 
-static int crc32c_cra_init(struct crypto_tfm *tfm)
+static int chksum_digest(struct shash_desc *desc, const u8 *data,
+                        unsigned int length, u8 *out)
 {
-       u32 *key = crypto_tfm_ctx(tfm);
+       struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm);
 
-       *key = ~0;
+       return __chksum_finup(&mctx->key, data, length, out);
+}
 
-       tfm->crt_ahash.reqsize = sizeof(u32);
+static int crc32c_cra_init(struct crypto_tfm *tfm)
+{
+       struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
 
+       mctx->key = ~0;
        return 0;
 }
 
-static struct crypto_alg alg = {
-       .cra_name               =       "crc32c",
-       .cra_driver_name        =       "crc32c-generic",
-       .cra_priority           =       100,
-       .cra_flags              =       CRYPTO_ALG_TYPE_AHASH,
-       .cra_blocksize          =       CHKSUM_BLOCK_SIZE,
-       .cra_alignmask          =       3,
-       .cra_ctxsize            =       sizeof(u32),
-       .cra_module             =       THIS_MODULE,
-       .cra_list               =       LIST_HEAD_INIT(alg.cra_list),
-       .cra_init               =       crc32c_cra_init,
-       .cra_type               =       &crypto_ahash_type,
-       .cra_u                  =       {
-               .ahash = {
-                        .digestsize    =       CHKSUM_DIGEST_SIZE,
-                        .setkey        =       crc32c_setkey,
-                        .init          =       crc32c_init,
-                        .update        =       crc32c_update,
-                        .final         =       crc32c_final,
-                        .digest        =       crc32c_digest,
-                }
+static struct shash_alg alg = {
+       .digestsize             =       CHKSUM_DIGEST_SIZE,
+       .setkey                 =       chksum_setkey,
+       .init                   =       chksum_init,
+       .update                 =       chksum_update,
+       .final                  =       chksum_final,
+       .finup                  =       chksum_finup,
+       .digest                 =       chksum_digest,
+       .descsize               =       sizeof(struct chksum_desc_ctx),
+       .base                   =       {
+               .cra_name               =       "crc32c",
+               .cra_driver_name        =       "crc32c-generic",
+               .cra_priority           =       100,
+               .cra_blocksize          =       CHKSUM_BLOCK_SIZE,
+               .cra_alignmask          =       3,
+               .cra_ctxsize            =       sizeof(struct chksum_ctx),
+               .cra_module             =       THIS_MODULE,
+               .cra_init               =       crc32c_cra_init,
        }
 };
 
 static int __init crc32c_mod_init(void)
 {
-       int err;
-
-       err = crypto_register_alg(&old_alg);
-       if (err)
-               return err;
-
-       err = crypto_register_alg(&alg);
-       if (err)
-               crypto_unregister_alg(&old_alg);
-
-       return err;
+       return crypto_register_shash(&alg);
 }
 
 static void __exit crc32c_mod_fini(void)
 {
-       crypto_unregister_alg(&alg);
-       crypto_unregister_alg(&old_alg);
+       crypto_unregister_shash(&alg);
 }
 
 module_init(crc32c_mod_init);
index 1f7d530..cb71c91 100644 (file)
@@ -17,6 +17,7 @@
  *
  */
 
+#include <crypto/internal/hash.h>
 #include <crypto/internal/skcipher.h>
 #include <linux/init.h>
 #include <linux/module.h>
@@ -38,15 +39,31 @@ static int null_compress(struct crypto_tfm *tfm, const u8 *src,
        return 0;
 }
 
-static void null_init(struct crypto_tfm *tfm)
-{ }
+static int null_init(struct shash_desc *desc)
+{
+       return 0;
+}
 
-static void null_update(struct crypto_tfm *tfm, const u8 *data,
-                       unsigned int len)
-{ }
+static int null_update(struct shash_desc *desc, const u8 *data,
+                      unsigned int len)
+{
+       return 0;
+}
 
-static void null_final(struct crypto_tfm *tfm, u8 *out)
-{ }
+static int null_final(struct shash_desc *desc, u8 *out)
+{
+       return 0;
+}
+
+static int null_digest(struct shash_desc *desc, const u8 *data,
+                      unsigned int len, u8 *out)
+{
+       return 0;
+}
+
+static int null_hash_setkey(struct crypto_shash *tfm, const u8 *key,
+                           unsigned int keylen)
+{ return 0; }
 
 static int null_setkey(struct crypto_tfm *tfm, const u8 *key,
                       unsigned int keylen)
@@ -89,19 +106,20 @@ static struct crypto_alg compress_null = {
        .coa_decompress         =       null_compress } }
 };
 
-static struct crypto_alg digest_null = {
-       .cra_name               =       "digest_null",
-       .cra_flags              =       CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize          =       NULL_BLOCK_SIZE,
-       .cra_ctxsize            =       0,
-       .cra_module             =       THIS_MODULE,
-       .cra_list               =       LIST_HEAD_INIT(digest_null.cra_list),   
-       .cra_u                  =       { .digest = {
-       .dia_digestsize         =       NULL_DIGEST_SIZE,
-       .dia_setkey             =       null_setkey,
-       .dia_init               =       null_init,
-       .dia_update             =       null_update,
-       .dia_final              =       null_final } }
+static struct shash_alg digest_null = {
+       .digestsize             =       NULL_DIGEST_SIZE,
+       .setkey                 =       null_hash_setkey,
+       .init                   =       null_init,
+       .update                 =       null_update,
+       .finup                  =       null_digest,
+       .digest                 =       null_digest,
+       .final                  =       null_final,
+       .base                   =       {
+               .cra_name               =       "digest_null",
+               .cra_flags              =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize          =       NULL_BLOCK_SIZE,
+               .cra_module             =       THIS_MODULE,
+       }
 };
 
 static struct crypto_alg cipher_null = {
@@ -154,7 +172,7 @@ static int __init crypto_null_mod_init(void)
        if (ret < 0)
                goto out_unregister_cipher;
 
-       ret = crypto_register_alg(&digest_null);
+       ret = crypto_register_shash(&digest_null);
        if (ret < 0)
                goto out_unregister_skcipher;
 
@@ -166,7 +184,7 @@ out:
        return ret;
 
 out_unregister_digest:
-       crypto_unregister_alg(&digest_null);
+       crypto_unregister_shash(&digest_null);
 out_unregister_skcipher:
        crypto_unregister_alg(&skcipher_null);
 out_unregister_cipher:
@@ -177,7 +195,7 @@ out_unregister_cipher:
 static void __exit crypto_null_mod_fini(void)
 {
        crypto_unregister_alg(&compress_null);
-       crypto_unregister_alg(&digest_null);
+       crypto_unregister_shash(&digest_null);
        crypto_unregister_alg(&skcipher_null);
        crypto_unregister_alg(&cipher_null);
 }
index 5d0e458..5bd3ee3 100644 (file)
@@ -868,9 +868,10 @@ static int des3_ede_setkey(struct crypto_tfm *tfm, const u8 *key,
        u32 *flags = &tfm->crt_flags;
 
        if (unlikely(!((K[0] ^ K[2]) | (K[1] ^ K[3])) ||
-                    !((K[2] ^ K[4]) | (K[3] ^ K[5]))))
+                    !((K[2] ^ K[4]) | (K[3] ^ K[5]))) &&
+                    (*flags & CRYPTO_TFM_REQ_WEAK_KEY))
        {
-               *flags |= CRYPTO_TFM_RES_BAD_KEY_SCHED;
+               *flags |= CRYPTO_TFM_RES_WEAK_KEY;
                return -EINVAL;
        }
 
index 1302f4c..b82d61f 100644 (file)
@@ -73,7 +73,7 @@ do {                                                          \
  * /afs/transarc.com/public/afsps/afs.rel31b.export-src/rxkad/sboxes.h
  */
 #undef Z
-#define Z(x) __constant_cpu_to_be32(x << 3)
+#define Z(x) cpu_to_be32(x << 3)
 static const __be32 sbox0[256] = {
        Z(0xea), Z(0x7f), Z(0xb2), Z(0x64), Z(0x9d), Z(0xb0), Z(0xd9), Z(0x11),
        Z(0xcd), Z(0x86), Z(0x86), Z(0x91), Z(0x0a), Z(0xb2), Z(0x93), Z(0x06),
@@ -110,7 +110,7 @@ static const __be32 sbox0[256] = {
 };
 
 #undef Z
-#define Z(x) __constant_cpu_to_be32((x << 27) | (x >> 5))
+#define Z(x) cpu_to_be32((x << 27) | (x >> 5))
 static const __be32 sbox1[256] = {
        Z(0x77), Z(0x14), Z(0xa6), Z(0xfe), Z(0xb2), Z(0x5e), Z(0x8c), Z(0x3e),
        Z(0x67), Z(0x6c), Z(0xa1), Z(0x0d), Z(0xc2), Z(0xa2), Z(0xc1), Z(0x85),
@@ -147,7 +147,7 @@ static const __be32 sbox1[256] = {
 };
 
 #undef Z
-#define Z(x) __constant_cpu_to_be32(x << 11)
+#define Z(x) cpu_to_be32(x << 11)
 static const __be32 sbox2[256] = {
        Z(0xf0), Z(0x37), Z(0x24), Z(0x53), Z(0x2a), Z(0x03), Z(0x83), Z(0x86),
        Z(0xd1), Z(0xec), Z(0x50), Z(0xf0), Z(0x42), Z(0x78), Z(0x2f), Z(0x6d),
@@ -184,7 +184,7 @@ static const __be32 sbox2[256] = {
 };
 
 #undef Z
-#define Z(x) __constant_cpu_to_be32(x << 19)
+#define Z(x) cpu_to_be32(x << 19)
 static const __be32 sbox3[256] = {
        Z(0xa9), Z(0x2a), Z(0x48), Z(0x51), Z(0x84), Z(0x7e), Z(0x49), Z(0xe2),
        Z(0xb5), Z(0xb7), Z(0x42), Z(0x33), Z(0x7d), Z(0x5d), Z(0xa6), Z(0x12),
index 7ff2d6a..0ad39c3 100644 (file)
@@ -16,7 +16,7 @@
  *
  */
 
-#include <crypto/algapi.h>
+#include <crypto/internal/hash.h>
 #include <crypto/scatterwalk.h>
 #include <linux/err.h>
 #include <linux/init.h>
@@ -238,9 +238,11 @@ static struct crypto_instance *hmac_alloc(struct rtattr **tb)
                return ERR_CAST(alg);
 
        inst = ERR_PTR(-EINVAL);
-       ds = (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
-            CRYPTO_ALG_TYPE_HASH ? alg->cra_hash.digestsize :
-                                   alg->cra_digest.dia_digestsize;
+       ds = alg->cra_type == &crypto_hash_type ?
+            alg->cra_hash.digestsize :
+            alg->cra_type ?
+            __crypto_shash_alg(alg)->digestsize :
+            alg->cra_digest.dia_digestsize;
        if (ds > alg->cra_blocksize)
                goto out_put_alg;
 
index 8ef72d7..3c19a27 100644 (file)
@@ -109,6 +109,8 @@ void crypto_alg_tested(const char *name, int err);
 void crypto_shoot_alg(struct crypto_alg *alg);
 struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type,
                                      u32 mask);
+struct crypto_tfm *crypto_create_tfm(struct crypto_alg *alg,
+                                    const struct crypto_type *frontend);
 
 int crypto_register_instance(struct crypto_template *tmpl,
                             struct crypto_instance *inst);
index 3c19aa0..7fca1f5 100644 (file)
@@ -20,8 +20,8 @@
  * (at your option) any later version.
  *
  */
+#include <crypto/internal/hash.h>
 #include <linux/init.h>
-#include <linux/crypto.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/types.h>
@@ -58,7 +58,7 @@ static inline u32 H(u32 x, u32 y, u32 z)
 {
        return x ^ y ^ z;
 }
-                        
+
 #define ROUND1(a,b,c,d,k,s) (a = lshift(a + F(b,c,d) + k, s))
 #define ROUND2(a,b,c,d,k,s) (a = lshift(a + G(b,c,d) + k + (u32)0x5A827999,s))
 #define ROUND3(a,b,c,d,k,s) (a = lshift(a + H(b,c,d) + k + (u32)0x6ED9EBA1,s))
@@ -148,24 +148,26 @@ static void md4_transform(u32 *hash, u32 const *in)
 
 static inline void md4_transform_helper(struct md4_ctx *ctx)
 {
-       le32_to_cpu_array(ctx->block, sizeof(ctx->block) / sizeof(u32));
+       le32_to_cpu_array(ctx->block, ARRAY_SIZE(ctx->block));
        md4_transform(ctx->hash, ctx->block);
 }
 
-static void md4_init(struct crypto_tfm *tfm)
+static int md4_init(struct shash_desc *desc)
 {
-       struct md4_ctx *mctx = crypto_tfm_ctx(tfm);
+       struct md4_ctx *mctx = shash_desc_ctx(desc);
 
        mctx->hash[0] = 0x67452301;
        mctx->hash[1] = 0xefcdab89;
        mctx->hash[2] = 0x98badcfe;
        mctx->hash[3] = 0x10325476;
        mctx->byte_count = 0;
+
+       return 0;
 }
 
-static void md4_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
+static int md4_update(struct shash_desc *desc, const u8 *data, unsigned int len)
 {
-       struct md4_ctx *mctx = crypto_tfm_ctx(tfm);
+       struct md4_ctx *mctx = shash_desc_ctx(desc);
        const u32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f);
 
        mctx->byte_count += len;
@@ -173,7 +175,7 @@ static void md4_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
        if (avail > len) {
                memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
                       data, len);
-               return;
+               return 0;
        }
 
        memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
@@ -191,11 +193,13 @@ static void md4_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
        }
 
        memcpy(mctx->block, data, len);
+
+       return 0;
 }
 
-static void md4_final(struct crypto_tfm *tfm, u8 *out)
+static int md4_final(struct shash_desc *desc, u8 *out)
 {
-       struct md4_ctx *mctx = crypto_tfm_ctx(tfm);
+       struct md4_ctx *mctx = shash_desc_ctx(desc);
        const unsigned int offset = mctx->byte_count & 0x3f;
        char *p = (char *)mctx->block + offset;
        int padding = 56 - (offset + 1);
@@ -214,33 +218,35 @@ static void md4_final(struct crypto_tfm *tfm, u8 *out)
        le32_to_cpu_array(mctx->block, (sizeof(mctx->block) -
                          sizeof(u64)) / sizeof(u32));
        md4_transform(mctx->hash, mctx->block);
-       cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(u32));
+       cpu_to_le32_array(mctx->hash, ARRAY_SIZE(mctx->hash));
        memcpy(out, mctx->hash, sizeof(mctx->hash));
        memset(mctx, 0, sizeof(*mctx));
+
+       return 0;
 }
 
-static struct crypto_alg alg = {
-       .cra_name       =       "md4",
-       .cra_flags      =       CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize  =       MD4_HMAC_BLOCK_SIZE,
-       .cra_ctxsize    =       sizeof(struct md4_ctx),
-       .cra_module     =       THIS_MODULE,
-       .cra_list       =       LIST_HEAD_INIT(alg.cra_list),   
-       .cra_u          =       { .digest = {
-       .dia_digestsize =       MD4_DIGEST_SIZE,
-       .dia_init       =       md4_init,
-       .dia_update     =       md4_update,
-       .dia_final      =       md4_final } }
+static struct shash_alg alg = {
+       .digestsize     =       MD4_DIGEST_SIZE,
+       .init           =       md4_init,
+       .update         =       md4_update,
+       .final          =       md4_final,
+       .descsize       =       sizeof(struct md4_ctx),
+       .base           =       {
+               .cra_name       =       "md4",
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       MD4_HMAC_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
 static int __init md4_mod_init(void)
 {
-       return crypto_register_alg(&alg);
+       return crypto_register_shash(&alg);
 }
 
 static void __exit md4_mod_fini(void)
 {
-       crypto_unregister_alg(&alg);
+       crypto_unregister_shash(&alg);
 }
 
 module_init(md4_mod_init);
index 39268f3..83eb529 100644 (file)
  * any later version.
  *
  */
+#include <crypto/internal/hash.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/string.h>
-#include <linux/crypto.h>
 #include <linux/types.h>
 #include <asm/byteorder.h>
 
@@ -147,20 +147,22 @@ static inline void md5_transform_helper(struct md5_ctx *ctx)
        md5_transform(ctx->hash, ctx->block);
 }
 
-static void md5_init(struct crypto_tfm *tfm)
+static int md5_init(struct shash_desc *desc)
 {
-       struct md5_ctx *mctx = crypto_tfm_ctx(tfm);
+       struct md5_ctx *mctx = shash_desc_ctx(desc);
 
        mctx->hash[0] = 0x67452301;
        mctx->hash[1] = 0xefcdab89;
        mctx->hash[2] = 0x98badcfe;
        mctx->hash[3] = 0x10325476;
        mctx->byte_count = 0;
+
+       return 0;
 }
 
-static void md5_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
+static int md5_update(struct shash_desc *desc, const u8 *data, unsigned int len)
 {
-       struct md5_ctx *mctx = crypto_tfm_ctx(tfm);
+       struct md5_ctx *mctx = shash_desc_ctx(desc);
        const u32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f);
 
        mctx->byte_count += len;
@@ -168,7 +170,7 @@ static void md5_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
        if (avail > len) {
                memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
                       data, len);
-               return;
+               return 0;
        }
 
        memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
@@ -186,11 +188,13 @@ static void md5_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
        }
 
        memcpy(mctx->block, data, len);
+
+       return 0;
 }
 
-static void md5_final(struct crypto_tfm *tfm, u8 *out)
+static int md5_final(struct shash_desc *desc, u8 *out)
 {
-       struct md5_ctx *mctx = crypto_tfm_ctx(tfm);
+       struct md5_ctx *mctx = shash_desc_ctx(desc);
        const unsigned int offset = mctx->byte_count & 0x3f;
        char *p = (char *)mctx->block + offset;
        int padding = 56 - (offset + 1);
@@ -212,30 +216,32 @@ static void md5_final(struct crypto_tfm *tfm, u8 *out)
        cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(u32));
        memcpy(out, mctx->hash, sizeof(mctx->hash));
        memset(mctx, 0, sizeof(*mctx));
+
+       return 0;
 }
 
-static struct crypto_alg alg = {
-       .cra_name       =       "md5",
-       .cra_flags      =       CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize  =       MD5_HMAC_BLOCK_SIZE,
-       .cra_ctxsize    =       sizeof(struct md5_ctx),
-       .cra_module     =       THIS_MODULE,
-       .cra_list       =       LIST_HEAD_INIT(alg.cra_list),
-       .cra_u          =       { .digest = {
-       .dia_digestsize =       MD5_DIGEST_SIZE,
-       .dia_init       =       md5_init,
-       .dia_update     =       md5_update,
-       .dia_final      =       md5_final } }
+static struct shash_alg alg = {
+       .digestsize     =       MD5_DIGEST_SIZE,
+       .init           =       md5_init,
+       .update         =       md5_update,
+       .final          =       md5_final,
+       .descsize       =       sizeof(struct md5_ctx),
+       .base           =       {
+               .cra_name       =       "md5",
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       MD5_HMAC_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
 static int __init md5_mod_init(void)
 {
-       return crypto_register_alg(&alg);
+       return crypto_register_shash(&alg);
 }
 
 static void __exit md5_mod_fini(void)
 {
-       crypto_unregister_alg(&alg);
+       crypto_unregister_shash(&alg);
 }
 
 module_init(md5_mod_init);
index 9e917b8..079b761 100644 (file)
@@ -9,23 +9,25 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-
+#include <crypto/internal/hash.h>
 #include <asm/byteorder.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/string.h>
-#include <linux/crypto.h>
 #include <linux/types.h>
 
 
 struct michael_mic_ctx {
+       u32 l, r;
+};
+
+struct michael_mic_desc_ctx {
        u8 pending[4];
        size_t pending_len;
 
        u32 l, r;
 };
 
-
 static inline u32 xswap(u32 val)
 {
        return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8);
@@ -45,17 +47,22 @@ do {                                \
 } while (0)
 
 
-static void michael_init(struct crypto_tfm *tfm)
+static int michael_init(struct shash_desc *desc)
 {
-       struct michael_mic_ctx *mctx = crypto_tfm_ctx(tfm);
+       struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc);
+       struct michael_mic_ctx *ctx = crypto_shash_ctx(desc->tfm);
        mctx->pending_len = 0;
+       mctx->l = ctx->l;
+       mctx->r = ctx->r;
+
+       return 0;
 }
 
 
-static void michael_update(struct crypto_tfm *tfm, const u8 *data,
+static int michael_update(struct shash_desc *desc, const u8 *data,
                           unsigned int len)
 {
-       struct michael_mic_ctx *mctx = crypto_tfm_ctx(tfm);
+       struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc);
        const __le32 *src;
 
        if (mctx->pending_len) {
@@ -68,7 +75,7 @@ static void michael_update(struct crypto_tfm *tfm, const u8 *data,
                len -= flen;
 
                if (mctx->pending_len < 4)
-                       return;
+                       return 0;
 
                src = (const __le32 *)mctx->pending;
                mctx->l ^= le32_to_cpup(src);
@@ -88,12 +95,14 @@ static void michael_update(struct crypto_tfm *tfm, const u8 *data,
                mctx->pending_len = len;
                memcpy(mctx->pending, src, len);
        }
+
+       return 0;
 }
 
 
-static void michael_final(struct crypto_tfm *tfm, u8 *out)
+static int michael_final(struct shash_desc *desc, u8 *out)
 {
-       struct michael_mic_ctx *mctx = crypto_tfm_ctx(tfm);
+       struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc);
        u8 *data = mctx->pending;
        __le32 *dst = (__le32 *)out;
 
@@ -119,17 +128,20 @@ static void michael_final(struct crypto_tfm *tfm, u8 *out)
 
        dst[0] = cpu_to_le32(mctx->l);
        dst[1] = cpu_to_le32(mctx->r);
+
+       return 0;
 }
 
 
-static int michael_setkey(struct crypto_tfm *tfm, const u8 *key,
+static int michael_setkey(struct crypto_shash *tfm, const u8 *key,
                          unsigned int keylen)
 {
-       struct michael_mic_ctx *mctx = crypto_tfm_ctx(tfm);
+       struct michael_mic_ctx *mctx = crypto_shash_ctx(tfm);
+
        const __le32 *data = (const __le32 *)key;
 
        if (keylen != 8) {
-               tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+               crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
                return -EINVAL;
        }
 
@@ -138,33 +150,31 @@ static int michael_setkey(struct crypto_tfm *tfm, const u8 *key,
        return 0;
 }
 
-
-static struct crypto_alg michael_mic_alg = {
-       .cra_name       = "michael_mic",
-       .cra_flags      = CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize  = 8,
-       .cra_ctxsize    = sizeof(struct michael_mic_ctx),
-       .cra_module     = THIS_MODULE,
-       .cra_alignmask  = 3,
-       .cra_list       = LIST_HEAD_INIT(michael_mic_alg.cra_list),
-       .cra_u          = { .digest = {
-       .dia_digestsize = 8,
-       .dia_init       = michael_init,
-       .dia_update     = michael_update,
-       .dia_final      = michael_final,
-       .dia_setkey     = michael_setkey } }
+static struct shash_alg alg = {
+       .digestsize             =       8,
+       .setkey                 =       michael_setkey,
+       .init                   =       michael_init,
+       .update                 =       michael_update,
+       .final                  =       michael_final,
+       .descsize               =       sizeof(struct michael_mic_desc_ctx),
+       .base                   =       {
+               .cra_name               =       "michael_mic",
+               .cra_blocksize          =       8,
+               .cra_alignmask          =       3,
+               .cra_ctxsize            =       sizeof(struct michael_mic_ctx),
+               .cra_module             =       THIS_MODULE,
+       }
 };
 
-
 static int __init michael_mic_init(void)
 {
-       return crypto_register_alg(&michael_mic_alg);
+       return crypto_register_shash(&alg);
 }
 
 
 static void __exit michael_mic_exit(void)
 {
-       crypto_unregister_alg(&michael_mic_alg);
+       crypto_unregister_shash(&alg);
 }
 
 
index 37a13d0..5dc07e4 100644 (file)
@@ -94,6 +94,17 @@ static int c_show(struct seq_file *m, void *p)
        seq_printf(m, "selftest     : %s\n",
                   (alg->cra_flags & CRYPTO_ALG_TESTED) ?
                   "passed" : "unknown");
+
+       if (alg->cra_flags & CRYPTO_ALG_LARVAL) {
+               seq_printf(m, "type         : larval\n");
+               seq_printf(m, "flags        : 0x%x\n", alg->cra_flags);
+               goto out;
+       }
+
+       if (alg->cra_type && alg->cra_type->show) {
+               alg->cra_type->show(m, alg);
+               goto out;
+       }
        
        switch (alg->cra_flags & (CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_LARVAL)) {
        case CRYPTO_ALG_TYPE_CIPHER:
@@ -115,16 +126,11 @@ static int c_show(struct seq_file *m, void *p)
                seq_printf(m, "type         : compression\n");
                break;
        default:
-               if (alg->cra_flags & CRYPTO_ALG_LARVAL) {
-                       seq_printf(m, "type         : larval\n");
-                       seq_printf(m, "flags        : 0x%x\n", alg->cra_flags);
-               } else if (alg->cra_type && alg->cra_type->show)
-                       alg->cra_type->show(m, alg);
-               else
-                       seq_printf(m, "type         : unknown\n");
+               seq_printf(m, "type         : unknown\n");
                break;
        }
 
+out:
        seq_putc(m, '\n');
        return 0;
 }
index 5de6fa2..1ceb673 100644 (file)
  * any later version.
  *
  */
+#include <crypto/internal/hash.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/crypto.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <asm/byteorder.h>
 
@@ -218,9 +217,9 @@ static void rmd128_transform(u32 *state, const __le32 *in)
        return;
 }
 
-static void rmd128_init(struct crypto_tfm *tfm)
+static int rmd128_init(struct shash_desc *desc)
 {
-       struct rmd128_ctx *rctx = crypto_tfm_ctx(tfm);
+       struct rmd128_ctx *rctx = shash_desc_ctx(desc);
 
        rctx->byte_count = 0;
 
@@ -230,12 +229,14 @@ static void rmd128_init(struct crypto_tfm *tfm)
        rctx->state[3] = RMD_H3;
 
        memset(rctx->buffer, 0, sizeof(rctx->buffer));
+
+       return 0;
 }
 
-static void rmd128_update(struct crypto_tfm *tfm, const u8 *data,
-                         unsigned int len)
+static int rmd128_update(struct shash_desc *desc, const u8 *data,
+                        unsigned int len)
 {
-       struct rmd128_ctx *rctx = crypto_tfm_ctx(tfm);
+       struct rmd128_ctx *rctx = shash_desc_ctx(desc);
        const u32 avail = sizeof(rctx->buffer) - (rctx->byte_count & 0x3f);
 
        rctx->byte_count += len;
@@ -244,7 +245,7 @@ static void rmd128_update(struct crypto_tfm *tfm, const u8 *data,
        if (avail > len) {
                memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
                       data, len);
-               return;
+               goto out;
        }
 
        memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
@@ -262,12 +263,15 @@ static void rmd128_update(struct crypto_tfm *tfm, const u8 *data,
        }
 
        memcpy(rctx->buffer, data, len);
+
+out:
+       return 0;
 }
 
 /* Add padding and return the message digest. */
-static void rmd128_final(struct crypto_tfm *tfm, u8 *out)
+static int rmd128_final(struct shash_desc *desc, u8 *out)
 {
-       struct rmd128_ctx *rctx = crypto_tfm_ctx(tfm);
+       struct rmd128_ctx *rctx = shash_desc_ctx(desc);
        u32 i, index, padlen;
        __le64 bits;
        __le32 *dst = (__le32 *)out;
@@ -278,10 +282,10 @@ static void rmd128_final(struct crypto_tfm *tfm, u8 *out)
        /* Pad out to 56 mod 64 */
        index = rctx->byte_count & 0x3f;
        padlen = (index < 56) ? (56 - index) : ((64+56) - index);
-       rmd128_update(tfm, padding, padlen);
+       rmd128_update(desc, padding, padlen);
 
        /* Append length */
-       rmd128_update(tfm, (const u8 *)&bits, sizeof(bits));
+       rmd128_update(desc, (const u8 *)&bits, sizeof(bits));
 
        /* Store state in digest */
        for (i = 0; i < 4; i++)
@@ -289,31 +293,32 @@ static void rmd128_final(struct crypto_tfm *tfm, u8 *out)
 
        /* Wipe context */
        memset(rctx, 0, sizeof(*rctx));
+
+       return 0;
 }
 
-static struct crypto_alg alg = {
-       .cra_name        =      "rmd128",
-       .cra_driver_name =      "rmd128",
-       .cra_flags       =      CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize   =      RMD128_BLOCK_SIZE,
-       .cra_ctxsize     =      sizeof(struct rmd128_ctx),
-       .cra_module      =      THIS_MODULE,
-       .cra_list        =      LIST_HEAD_INIT(alg.cra_list),
-       .cra_u           =      { .digest = {
-       .dia_digestsize  =      RMD128_DIGEST_SIZE,
-       .dia_init        =      rmd128_init,
-       .dia_update      =      rmd128_update,
-       .dia_final       =      rmd128_final } }
+static struct shash_alg alg = {
+       .digestsize     =       RMD128_DIGEST_SIZE,
+       .init           =       rmd128_init,
+       .update         =       rmd128_update,
+       .final          =       rmd128_final,
+       .descsize       =       sizeof(struct rmd128_ctx),
+       .base           =       {
+               .cra_name        =      "rmd128",
+               .cra_flags       =      CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize   =      RMD128_BLOCK_SIZE,
+               .cra_module      =      THIS_MODULE,
+       }
 };
 
 static int __init rmd128_mod_init(void)
 {
-       return crypto_register_alg(&alg);
+       return crypto_register_shash(&alg);
 }
 
 static void __exit rmd128_mod_fini(void)
 {
-       crypto_unregister_alg(&alg);
+       crypto_unregister_shash(&alg);
 }
 
 module_init(rmd128_mod_init);
@@ -321,5 +326,3 @@ module_exit(rmd128_mod_fini);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("RIPEMD-128 Message Digest");
-
-MODULE_ALIAS("rmd128");
index f001ec7..472261f 100644 (file)
  * any later version.
  *
  */
+#include <crypto/internal/hash.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/crypto.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <asm/byteorder.h>
 
@@ -261,9 +260,9 @@ static void rmd160_transform(u32 *state, const __le32 *in)
        return;
 }
 
-static void rmd160_init(struct crypto_tfm *tfm)
+static int rmd160_init(struct shash_desc *desc)
 {
-       struct rmd160_ctx *rctx = crypto_tfm_ctx(tfm);
+       struct rmd160_ctx *rctx = shash_desc_ctx(desc);
 
        rctx->byte_count = 0;
 
@@ -274,12 +273,14 @@ static void rmd160_init(struct crypto_tfm *tfm)
        rctx->state[4] = RMD_H4;
 
        memset(rctx->buffer, 0, sizeof(rctx->buffer));
+
+       return 0;
 }
 
-static void rmd160_update(struct crypto_tfm *tfm, const u8 *data,
-                         unsigned int len)
+static int rmd160_update(struct shash_desc *desc, const u8 *data,
+                        unsigned int len)
 {
-       struct rmd160_ctx *rctx = crypto_tfm_ctx(tfm);
+       struct rmd160_ctx *rctx = shash_desc_ctx(desc);
        const u32 avail = sizeof(rctx->buffer) - (rctx->byte_count & 0x3f);
 
        rctx->byte_count += len;
@@ -288,7 +289,7 @@ static void rmd160_update(struct crypto_tfm *tfm, const u8 *data,
        if (avail > len) {
                memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
                       data, len);
-               return;
+               goto out;
        }
 
        memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
@@ -306,12 +307,15 @@ static void rmd160_update(struct crypto_tfm *tfm, const u8 *data,
        }
 
        memcpy(rctx->buffer, data, len);
+
+out:
+       return 0;
 }
 
 /* Add padding and return the message digest. */
-static void rmd160_final(struct crypto_tfm *tfm, u8 *out)
+static int rmd160_final(struct shash_desc *desc, u8 *out)
 {
-       struct rmd160_ctx *rctx = crypto_tfm_ctx(tfm);
+       struct rmd160_ctx *rctx = shash_desc_ctx(desc);
        u32 i, index, padlen;
        __le64 bits;
        __le32 *dst = (__le32 *)out;
@@ -322,10 +326,10 @@ static void rmd160_final(struct crypto_tfm *tfm, u8 *out)
        /* Pad out to 56 mod 64 */
        index = rctx->byte_count & 0x3f;
        padlen = (index < 56) ? (56 - index) : ((64+56) - index);
-       rmd160_update(tfm, padding, padlen);
+       rmd160_update(desc, padding, padlen);
 
        /* Append length */
-       rmd160_update(tfm, (const u8 *)&bits, sizeof(bits));
+       rmd160_update(desc, (const u8 *)&bits, sizeof(bits));
 
        /* Store state in digest */
        for (i = 0; i < 5; i++)
@@ -333,31 +337,32 @@ static void rmd160_final(struct crypto_tfm *tfm, u8 *out)
 
        /* Wipe context */
        memset(rctx, 0, sizeof(*rctx));
+
+       return 0;
 }
 
-static struct crypto_alg alg = {
-       .cra_name        =      "rmd160",
-       .cra_driver_name =      "rmd160",
-       .cra_flags       =      CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize   =      RMD160_BLOCK_SIZE,
-       .cra_ctxsize     =      sizeof(struct rmd160_ctx),
-       .cra_module      =      THIS_MODULE,
-       .cra_list        =      LIST_HEAD_INIT(alg.cra_list),
-       .cra_u           =      { .digest = {
-       .dia_digestsize  =      RMD160_DIGEST_SIZE,
-       .dia_init        =      rmd160_init,
-       .dia_update      =      rmd160_update,
-       .dia_final       =      rmd160_final } }
+static struct shash_alg alg = {
+       .digestsize     =       RMD160_DIGEST_SIZE,
+       .init           =       rmd160_init,
+       .update         =       rmd160_update,
+       .final          =       rmd160_final,
+       .descsize       =       sizeof(struct rmd160_ctx),
+       .base           =       {
+               .cra_name        =      "rmd160",
+               .cra_flags       =      CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize   =      RMD160_BLOCK_SIZE,
+               .cra_module      =      THIS_MODULE,
+       }
 };
 
 static int __init rmd160_mod_init(void)
 {
-       return crypto_register_alg(&alg);
+       return crypto_register_shash(&alg);
 }
 
 static void __exit rmd160_mod_fini(void)
 {
-       crypto_unregister_alg(&alg);
+       crypto_unregister_shash(&alg);
 }
 
 module_init(rmd160_mod_init);
@@ -365,5 +370,3 @@ module_exit(rmd160_mod_fini);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("RIPEMD-160 Message Digest");
-
-MODULE_ALIAS("rmd160");
index e3de5b4..72eafa8 100644 (file)
  * any later version.
  *
  */
+#include <crypto/internal/hash.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/crypto.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <asm/byteorder.h>
 
@@ -233,9 +232,9 @@ static void rmd256_transform(u32 *state, const __le32 *in)
        return;
 }
 
-static void rmd256_init(struct crypto_tfm *tfm)
+static int rmd256_init(struct shash_desc *desc)
 {
-       struct rmd256_ctx *rctx = crypto_tfm_ctx(tfm);
+       struct rmd256_ctx *rctx = shash_desc_ctx(desc);
 
        rctx->byte_count = 0;
 
@@ -249,12 +248,14 @@ static void rmd256_init(struct crypto_tfm *tfm)
        rctx->state[7] = RMD_H8;
 
        memset(rctx->buffer, 0, sizeof(rctx->buffer));
+
+       return 0;
 }
 
-static void rmd256_update(struct crypto_tfm *tfm, const u8 *data,
-                         unsigned int len)
+static int rmd256_update(struct shash_desc *desc, const u8 *data,
+                        unsigned int len)
 {
-       struct rmd256_ctx *rctx = crypto_tfm_ctx(tfm);
+       struct rmd256_ctx *rctx = shash_desc_ctx(desc);
        const u32 avail = sizeof(rctx->buffer) - (rctx->byte_count & 0x3f);
 
        rctx->byte_count += len;
@@ -263,7 +264,7 @@ static void rmd256_update(struct crypto_tfm *tfm, const u8 *data,
        if (avail > len) {
                memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
                       data, len);
-               return;
+               goto out;
        }
 
        memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
@@ -281,12 +282,15 @@ static void rmd256_update(struct crypto_tfm *tfm, const u8 *data,
        }
 
        memcpy(rctx->buffer, data, len);
+
+out:
+       return 0;
 }
 
 /* Add padding and return the message digest. */
-static void rmd256_final(struct crypto_tfm *tfm, u8 *out)
+static int rmd256_final(struct shash_desc *desc, u8 *out)
 {
-       struct rmd256_ctx *rctx = crypto_tfm_ctx(tfm);
+       struct rmd256_ctx *rctx = shash_desc_ctx(desc);
        u32 i, index, padlen;
        __le64 bits;
        __le32 *dst = (__le32 *)out;
@@ -297,10 +301,10 @@ static void rmd256_final(struct crypto_tfm *tfm, u8 *out)
        /* Pad out to 56 mod 64 */
        index = rctx->byte_count & 0x3f;
        padlen = (index < 56) ? (56 - index) : ((64+56) - index);
-       rmd256_update(tfm, padding, padlen);
+       rmd256_update(desc, padding, padlen);
 
        /* Append length */
-       rmd256_update(tfm, (const u8 *)&bits, sizeof(bits));
+       rmd256_update(desc, (const u8 *)&bits, sizeof(bits));
 
        /* Store state in digest */
        for (i = 0; i < 8; i++)
@@ -308,31 +312,32 @@ static void rmd256_final(struct crypto_tfm *tfm, u8 *out)
 
        /* Wipe context */
        memset(rctx, 0, sizeof(*rctx));
+
+       return 0;
 }
 
-static struct crypto_alg alg = {
-       .cra_name        =      "rmd256",
-       .cra_driver_name =      "rmd256",
-       .cra_flags       =      CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize   =      RMD256_BLOCK_SIZE,
-       .cra_ctxsize     =      sizeof(struct rmd256_ctx),
-       .cra_module      =      THIS_MODULE,
-       .cra_list        =      LIST_HEAD_INIT(alg.cra_list),
-       .cra_u           =      { .digest = {
-       .dia_digestsize  =      RMD256_DIGEST_SIZE,
-       .dia_init        =      rmd256_init,
-       .dia_update      =      rmd256_update,
-       .dia_final       =      rmd256_final } }
+static struct shash_alg alg = {
+       .digestsize     =       RMD256_DIGEST_SIZE,
+       .init           =       rmd256_init,
+       .update         =       rmd256_update,
+       .final          =       rmd256_final,
+       .descsize       =       sizeof(struct rmd256_ctx),
+       .base           =       {
+               .cra_name        =      "rmd256",
+               .cra_flags       =      CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize   =      RMD256_BLOCK_SIZE,
+               .cra_module      =      THIS_MODULE,
+       }
 };
 
 static int __init rmd256_mod_init(void)
 {
-       return crypto_register_alg(&alg);
+       return crypto_register_shash(&alg);
 }
 
 static void __exit rmd256_mod_fini(void)
 {
-       crypto_unregister_alg(&alg);
+       crypto_unregister_shash(&alg);
 }
 
 module_init(rmd256_mod_init);
@@ -340,5 +345,3 @@ module_exit(rmd256_mod_fini);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("RIPEMD-256 Message Digest");
-
-MODULE_ALIAS("rmd256");
index b143d66..86becab 100644 (file)
  * any later version.
  *
  */
+#include <crypto/internal/hash.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/crypto.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <asm/byteorder.h>
 
@@ -280,9 +279,9 @@ static void rmd320_transform(u32 *state, const __le32 *in)
        return;
 }
 
-static void rmd320_init(struct crypto_tfm *tfm)
+static int rmd320_init(struct shash_desc *desc)
 {
-       struct rmd320_ctx *rctx = crypto_tfm_ctx(tfm);
+       struct rmd320_ctx *rctx = shash_desc_ctx(desc);
 
        rctx->byte_count = 0;
 
@@ -298,12 +297,14 @@ static void rmd320_init(struct crypto_tfm *tfm)
        rctx->state[9] = RMD_H9;
 
        memset(rctx->buffer, 0, sizeof(rctx->buffer));
+
+       return 0;
 }
 
-static void rmd320_update(struct crypto_tfm *tfm, const u8 *data,
-                         unsigned int len)
+static int rmd320_update(struct shash_desc *desc, const u8 *data,
+                        unsigned int len)
 {
-       struct rmd320_ctx *rctx = crypto_tfm_ctx(tfm);
+       struct rmd320_ctx *rctx = shash_desc_ctx(desc);
        const u32 avail = sizeof(rctx->buffer) - (rctx->byte_count & 0x3f);
 
        rctx->byte_count += len;
@@ -312,7 +313,7 @@ static void rmd320_update(struct crypto_tfm *tfm, const u8 *data,
        if (avail > len) {
                memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
                       data, len);
-               return;
+               goto out;
        }
 
        memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
@@ -330,12 +331,15 @@ static void rmd320_update(struct crypto_tfm *tfm, const u8 *data,
        }
 
        memcpy(rctx->buffer, data, len);
+
+out:
+       return 0;
 }
 
 /* Add padding and return the message digest. */
-static void rmd320_final(struct crypto_tfm *tfm, u8 *out)
+static int rmd320_final(struct shash_desc *desc, u8 *out)
 {
-       struct rmd320_ctx *rctx = crypto_tfm_ctx(tfm);
+       struct rmd320_ctx *rctx = shash_desc_ctx(desc);
        u32 i, index, padlen;
        __le64 bits;
        __le32 *dst = (__le32 *)out;
@@ -346,10 +350,10 @@ static void rmd320_final(struct crypto_tfm *tfm, u8 *out)
        /* Pad out to 56 mod 64 */
        index = rctx->byte_count & 0x3f;
        padlen = (index < 56) ? (56 - index) : ((64+56) - index);
-       rmd320_update(tfm, padding, padlen);
+       rmd320_update(desc, padding, padlen);
 
        /* Append length */
-       rmd320_update(tfm, (const u8 *)&bits, sizeof(bits));
+       rmd320_update(desc, (const u8 *)&bits, sizeof(bits));
 
        /* Store state in digest */
        for (i = 0; i < 10; i++)
@@ -357,31 +361,32 @@ static void rmd320_final(struct crypto_tfm *tfm, u8 *out)
 
        /* Wipe context */
        memset(rctx, 0, sizeof(*rctx));
+
+       return 0;
 }
 
-static struct crypto_alg alg = {
-       .cra_name        =      "rmd320",
-       .cra_driver_name =      "rmd320",
-       .cra_flags       =      CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize   =      RMD320_BLOCK_SIZE,
-       .cra_ctxsize     =      sizeof(struct rmd320_ctx),
-       .cra_module      =      THIS_MODULE,
-       .cra_list        =      LIST_HEAD_INIT(alg.cra_list),
-       .cra_u           =      { .digest = {
-       .dia_digestsize  =      RMD320_DIGEST_SIZE,
-       .dia_init        =      rmd320_init,
-       .dia_update      =      rmd320_update,
-       .dia_final       =      rmd320_final } }
+static struct shash_alg alg = {
+       .digestsize     =       RMD320_DIGEST_SIZE,
+       .init           =       rmd320_init,
+       .update         =       rmd320_update,
+       .final          =       rmd320_final,
+       .descsize       =       sizeof(struct rmd320_ctx),
+       .base           =       {
+               .cra_name        =      "rmd320",
+               .cra_flags       =      CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize   =      RMD320_BLOCK_SIZE,
+               .cra_module      =      THIS_MODULE,
+       }
 };
 
 static int __init rmd320_mod_init(void)
 {
-       return crypto_register_alg(&alg);
+       return crypto_register_shash(&alg);
 }
 
 static void __exit rmd320_mod_fini(void)
 {
-       crypto_unregister_alg(&alg);
+       crypto_unregister_shash(&alg);
 }
 
 module_init(rmd320_mod_init);
@@ -389,5 +394,3 @@ module_exit(rmd320_mod_fini);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("RIPEMD-320 Message Digest");
-
-MODULE_ALIAS("rmd320");
index b07d559..eac10c1 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/errno.h>
 #include <linux/crypto.h>
 #include <linux/types.h>
+#include <linux/bitops.h>
 #include <crypto/algapi.h>
 #include <asm/byteorder.h>
 
@@ -42,10 +43,6 @@ D. J. Bernstein
 Public domain.
 */
 
-#define ROTATE(v,n) (((v) << (n)) | ((v) >> (32 - (n))))
-#define XOR(v,w) ((v) ^ (w))
-#define PLUS(v,w) (((v) + (w)))
-#define PLUSONE(v) (PLUS((v),1))
 #define U32TO8_LITTLE(p, v) \
        { (p)[0] = (v >>  0) & 0xff; (p)[1] = (v >>  8) & 0xff; \
          (p)[2] = (v >> 16) & 0xff; (p)[3] = (v >> 24) & 0xff; }
@@ -65,41 +62,41 @@ static void salsa20_wordtobyte(u8 output[64], const u32 input[16])
 
        memcpy(x, input, sizeof(x));
        for (i = 20; i > 0; i -= 2) {
-               x[ 4] = XOR(x[ 4],ROTATE(PLUS(x[ 0],x[12]), 7));
-               x[ 8] = XOR(x[ 8],ROTATE(PLUS(x[ 4],x[ 0]), 9));
-               x[12] = XOR(x[12],ROTATE(PLUS(x[ 8],x[ 4]),13));
-               x[ 0] = XOR(x[ 0],ROTATE(PLUS(x[12],x[ 8]),18));
-               x[ 9] = XOR(x[ 9],ROTATE(PLUS(x[ 5],x[ 1]), 7));
-               x[13] = XOR(x[13],ROTATE(PLUS(x[ 9],x[ 5]), 9));
-               x[ 1] = XOR(x[ 1],ROTATE(PLUS(x[13],x[ 9]),13));
-               x[ 5] = XOR(x[ 5],ROTATE(PLUS(x[ 1],x[13]),18));
-               x[14] = XOR(x[14],ROTATE(PLUS(x[10],x[ 6]), 7));
-               x[ 2] = XOR(x[ 2],ROTATE(PLUS(x[14],x[10]), 9));
-               x[ 6] = XOR(x[ 6],ROTATE(PLUS(x[ 2],x[14]),13));
-               x[10] = XOR(x[10],ROTATE(PLUS(x[ 6],x[ 2]),18));
-               x[ 3] = XOR(x[ 3],ROTATE(PLUS(x[15],x[11]), 7));
-               x[ 7] = XOR(x[ 7],ROTATE(PLUS(x[ 3],x[15]), 9));
-               x[11] = XOR(x[11],ROTATE(PLUS(x[ 7],x[ 3]),13));
-               x[15] = XOR(x[15],ROTATE(PLUS(x[11],x[ 7]),18));
-               x[ 1] = XOR(x[ 1],ROTATE(PLUS(x[ 0],x[ 3]), 7));
-               x[ 2] = XOR(x[ 2],ROTATE(PLUS(x[ 1],x[ 0]), 9));
-               x[ 3] = XOR(x[ 3],ROTATE(PLUS(x[ 2],x[ 1]),13));
-               x[ 0] = XOR(x[ 0],ROTATE(PLUS(x[ 3],x[ 2]),18));
-               x[ 6] = XOR(x[ 6],ROTATE(PLUS(x[ 5],x[ 4]), 7));
-               x[ 7] = XOR(x[ 7],ROTATE(PLUS(x[ 6],x[ 5]), 9));
-               x[ 4] = XOR(x[ 4],ROTATE(PLUS(x[ 7],x[ 6]),13));
-               x[ 5] = XOR(x[ 5],ROTATE(PLUS(x[ 4],x[ 7]),18));
-               x[11] = XOR(x[11],ROTATE(PLUS(x[10],x[ 9]), 7));
-               x[ 8] = XOR(x[ 8],ROTATE(PLUS(x[11],x[10]), 9));
-               x[ 9] = XOR(x[ 9],ROTATE(PLUS(x[ 8],x[11]),13));
-               x[10] = XOR(x[10],ROTATE(PLUS(x[ 9],x[ 8]),18));
-               x[12] = XOR(x[12],ROTATE(PLUS(x[15],x[14]), 7));
-               x[13] = XOR(x[13],ROTATE(PLUS(x[12],x[15]), 9));
-               x[14] = XOR(x[14],ROTATE(PLUS(x[13],x[12]),13));
-               x[15] = XOR(x[15],ROTATE(PLUS(x[14],x[13]),18));
+               x[ 4] ^= rol32((x[ 0] + x[12]),  7);
+               x[ 8] ^= rol32((x[ 4] + x[ 0]),  9);
+               x[12] ^= rol32((x[ 8] + x[ 4]), 13);
+               x[ 0] ^= rol32((x[12] + x[ 8]), 18);
+               x[ 9] ^= rol32((x[ 5] + x[ 1]),  7);
+               x[13] ^= rol32((x[ 9] + x[ 5]),  9);
+               x[ 1] ^= rol32((x[13] + x[ 9]), 13);
+               x[ 5] ^= rol32((x[ 1] + x[13]), 18);
+               x[14] ^= rol32((x[10] + x[ 6]),  7);
+               x[ 2] ^= rol32((x[14] + x[10]),  9);
+               x[ 6] ^= rol32((x[ 2] + x[14]), 13);
+               x[10] ^= rol32((x[ 6] + x[ 2]), 18);
+               x[ 3] ^= rol32((x[15] + x[11]),  7);
+               x[ 7] ^= rol32((x[ 3] + x[15]),  9);
+               x[11] ^= rol32((x[ 7] + x[ 3]), 13);
+               x[15] ^= rol32((x[11] + x[ 7]), 18);
+               x[ 1] ^= rol32((x[ 0] + x[ 3]),  7);
+               x[ 2] ^= rol32((x[ 1] + x[ 0]),  9);
+               x[ 3] ^= rol32((x[ 2] + x[ 1]), 13);
+               x[ 0] ^= rol32((x[ 3] + x[ 2]), 18);
+               x[ 6] ^= rol32((x[ 5] + x[ 4]),  7);
+               x[ 7] ^= rol32((x[ 6] + x[ 5]),  9);
+               x[ 4] ^= rol32((x[ 7] + x[ 6]), 13);
+               x[ 5] ^= rol32((x[ 4] + x[ 7]), 18);
+               x[11] ^= rol32((x[10] + x[ 9]),  7);
+               x[ 8] ^= rol32((x[11] + x[10]),  9);
+               x[ 9] ^= rol32((x[ 8] + x[11]), 13);
+               x[10] ^= rol32((x[ 9] + x[ 8]), 18);
+               x[12] ^= rol32((x[15] + x[14]),  7);
+               x[13] ^= rol32((x[12] + x[15]),  9);
+               x[14] ^= rol32((x[13] + x[12]), 13);
+               x[15] ^= rol32((x[14] + x[13]), 18);
        }
        for (i = 0; i < 16; ++i)
-               x[i] = PLUS(x[i],input[i]);
+               x[i] += input[i];
        for (i = 0; i < 16; ++i)
                U32TO8_LITTLE(output + 4 * i,x[i]);
 }
@@ -150,9 +147,9 @@ static void salsa20_encrypt_bytes(struct salsa20_ctx *ctx, u8 *dst,
        while (bytes) {
                salsa20_wordtobyte(buf, ctx->input);
 
-               ctx->input[8] = PLUSONE(ctx->input[8]);
+               ctx->input[8]++;
                if (!ctx->input[8])
-                       ctx->input[9] = PLUSONE(ctx->input[9]);
+                       ctx->input[9]++;
 
                if (bytes <= 64) {
                        crypto_xor(dst, buf, bytes);
index c7c6899..9efef20 100644 (file)
  * any later version.
  *
  */
+#include <crypto/internal/hash.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/crypto.h>
 #include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <crypto/sha.h>
@@ -31,9 +31,10 @@ struct sha1_ctx {
         u8 buffer[64];
 };
 
-static void sha1_init(struct crypto_tfm *tfm)
+static int sha1_init(struct shash_desc *desc)
 {
-       struct sha1_ctx *sctx = crypto_tfm_ctx(tfm);
+       struct sha1_ctx *sctx = shash_desc_ctx(desc);
+
        static const struct sha1_ctx initstate = {
          0,
          { SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4 },
@@ -41,12 +42,14 @@ static void sha1_init(struct crypto_tfm *tfm)
        };
 
        *sctx = initstate;
+
+       return 0;
 }
 
-static void sha1_update(struct crypto_tfm *tfm, const u8 *data,
+static int sha1_update(struct shash_desc *desc, const u8 *data,
                        unsigned int len)
 {
-       struct sha1_ctx *sctx = crypto_tfm_ctx(tfm);
+       struct sha1_ctx *sctx = shash_desc_ctx(desc);
        unsigned int partial, done;
        const u8 *src;
 
@@ -74,13 +77,15 @@ static void sha1_update(struct crypto_tfm *tfm, const u8 *data,
                partial = 0;
        }
        memcpy(sctx->buffer + partial, src, len - done);
+
+       return 0;
 }
 
 
 /* Add padding and return the message digest. */
-static void sha1_final(struct crypto_tfm *tfm, u8 *out)
+static int sha1_final(struct shash_desc *desc, u8 *out)
 {
-       struct sha1_ctx *sctx = crypto_tfm_ctx(tfm);
+       struct sha1_ctx *sctx = shash_desc_ctx(desc);
        __be32 *dst = (__be32 *)out;
        u32 i, index, padlen;
        __be64 bits;
@@ -91,10 +96,10 @@ static void sha1_final(struct crypto_tfm *tfm, u8 *out)
        /* Pad out to 56 mod 64 */
        index = sctx->count & 0x3f;
        padlen = (index < 56) ? (56 - index) : ((64+56) - index);
-       sha1_update(tfm, padding, padlen);
+       sha1_update(desc, padding, padlen);
 
        /* Append length */
-       sha1_update(tfm, (const u8 *)&bits, sizeof(bits));
+       sha1_update(desc, (const u8 *)&bits, sizeof(bits));
 
        /* Store state in digest */
        for (i = 0; i < 5; i++)
@@ -102,32 +107,33 @@ static void sha1_final(struct crypto_tfm *tfm, u8 *out)
 
        /* Wipe context */
        memset(sctx, 0, sizeof *sctx);
+
+       return 0;
 }
 
-static struct crypto_alg alg = {
-       .cra_name       =       "sha1",
-       .cra_driver_name=       "sha1-generic",
-       .cra_flags      =       CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize  =       SHA1_BLOCK_SIZE,
-       .cra_ctxsize    =       sizeof(struct sha1_ctx),
-       .cra_module     =       THIS_MODULE,
-       .cra_alignmask  =       3,
-       .cra_list       =       LIST_HEAD_INIT(alg.cra_list),
-       .cra_u          =       { .digest = {
-       .dia_digestsize =       SHA1_DIGEST_SIZE,
-       .dia_init       =       sha1_init,
-       .dia_update     =       sha1_update,
-       .dia_final      =       sha1_final } }
+static struct shash_alg alg = {
+       .digestsize     =       SHA1_DIGEST_SIZE,
+       .init           =       sha1_init,
+       .update         =       sha1_update,
+       .final          =       sha1_final,
+       .descsize       =       sizeof(struct sha1_ctx),
+       .base           =       {
+               .cra_name       =       "sha1",
+               .cra_driver_name=       "sha1-generic",
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA1_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
 static int __init sha1_generic_mod_init(void)
 {
-       return crypto_register_alg(&alg);
+       return crypto_register_shash(&alg);
 }
 
 static void __exit sha1_generic_mod_fini(void)
 {
-       crypto_unregister_alg(&alg);
+       crypto_unregister_shash(&alg);
 }
 
 module_init(sha1_generic_mod_init);
index 5a8dd47..caa3542 100644 (file)
  * any later version.
  *
  */
+#include <crypto/internal/hash.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/crypto.h>
 #include <linux/types.h>
 #include <crypto/sha.h>
 #include <asm/byteorder.h>
@@ -69,7 +69,7 @@ static void sha256_transform(u32 *state, const u8 *input)
        /* now blend */
        for (i = 16; i < 64; i++)
                BLEND_OP(i, W);
-    
+
        /* load the state into our registers */
        a=state[0];  b=state[1];  c=state[2];  d=state[3];
        e=state[4];  f=state[5];  g=state[6];  h=state[7];
@@ -220,9 +220,9 @@ static void sha256_transform(u32 *state, const u8 *input)
 }
 
 
-static void sha224_init(struct crypto_tfm *tfm)
+static int sha224_init(struct shash_desc *desc)
 {
-       struct sha256_ctx *sctx = crypto_tfm_ctx(tfm);
+       struct sha256_ctx *sctx = shash_desc_ctx(desc);
        sctx->state[0] = SHA224_H0;
        sctx->state[1] = SHA224_H1;
        sctx->state[2] = SHA224_H2;
@@ -233,11 +233,13 @@ static void sha224_init(struct crypto_tfm *tfm)
        sctx->state[7] = SHA224_H7;
        sctx->count[0] = 0;
        sctx->count[1] = 0;
+
+       return 0;
 }
 
-static void sha256_init(struct crypto_tfm *tfm)
+static int sha256_init(struct shash_desc *desc)
 {
-       struct sha256_ctx *sctx = crypto_tfm_ctx(tfm);
+       struct sha256_ctx *sctx = shash_desc_ctx(desc);
        sctx->state[0] = SHA256_H0;
        sctx->state[1] = SHA256_H1;
        sctx->state[2] = SHA256_H2;
@@ -247,12 +249,14 @@ static void sha256_init(struct crypto_tfm *tfm)
        sctx->state[6] = SHA256_H6;
        sctx->state[7] = SHA256_H7;
        sctx->count[0] = sctx->count[1] = 0;
+
+       return 0;
 }
 
-static void sha256_update(struct crypto_tfm *tfm, const u8 *data,
+static int sha256_update(struct shash_desc *desc, const u8 *data,
                          unsigned int len)
 {
-       struct sha256_ctx *sctx = crypto_tfm_ctx(tfm);
+       struct sha256_ctx *sctx = shash_desc_ctx(desc);
        unsigned int i, index, part_len;
 
        /* Compute number of bytes mod 128 */
@@ -277,14 +281,16 @@ static void sha256_update(struct crypto_tfm *tfm, const u8 *data,
        } else {
                i = 0;
        }
-       
+
        /* Buffer remaining input */
        memcpy(&sctx->buf[index], &data[i], len-i);
+
+       return 0;
 }
 
-static void sha256_final(struct crypto_tfm *tfm, u8 *out)
+static int sha256_final(struct shash_desc *desc, u8 *out)
 {
-       struct sha256_ctx *sctx = crypto_tfm_ctx(tfm);
+       struct sha256_ctx *sctx = shash_desc_ctx(desc);
        __be32 *dst = (__be32 *)out;
        __be32 bits[2];
        unsigned int index, pad_len;
@@ -298,10 +304,10 @@ static void sha256_final(struct crypto_tfm *tfm, u8 *out)
        /* Pad out to 56 mod 64. */
        index = (sctx->count[0] >> 3) & 0x3f;
        pad_len = (index < 56) ? (56 - index) : ((64+56) - index);
-       sha256_update(tfm, padding, pad_len);
+       sha256_update(desc, padding, pad_len);
 
        /* Append length (before padding) */
-       sha256_update(tfm, (const u8 *)bits, sizeof(bits));
+       sha256_update(desc, (const u8 *)bits, sizeof(bits));
 
        /* Store state in digest */
        for (i = 0; i < 8; i++)
@@ -309,71 +315,73 @@ static void sha256_final(struct crypto_tfm *tfm, u8 *out)
 
        /* Zeroize sensitive information. */
        memset(sctx, 0, sizeof(*sctx));
+
+       return 0;
 }
 
-static void sha224_final(struct crypto_tfm *tfm, u8 *hash)
+static int sha224_final(struct shash_desc *desc, u8 *hash)
 {
        u8 D[SHA256_DIGEST_SIZE];
 
-       sha256_final(tfm, D);
+       sha256_final(desc, D);
 
        memcpy(hash, D, SHA224_DIGEST_SIZE);
        memset(D, 0, SHA256_DIGEST_SIZE);
+
+       return 0;
 }
 
-static struct crypto_alg sha256 = {
-       .cra_name       =       "sha256",
-       .cra_driver_name=       "sha256-generic",
-       .cra_flags      =       CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize  =       SHA256_BLOCK_SIZE,
-       .cra_ctxsize    =       sizeof(struct sha256_ctx),
-       .cra_module     =       THIS_MODULE,
-       .cra_alignmask  =       3,
-       .cra_list       =       LIST_HEAD_INIT(sha256.cra_list),
-       .cra_u          =       { .digest = {
-       .dia_digestsize =       SHA256_DIGEST_SIZE,
-       .dia_init       =       sha256_init,
-       .dia_update     =       sha256_update,
-       .dia_final      =       sha256_final } }
+static struct shash_alg sha256 = {
+       .digestsize     =       SHA256_DIGEST_SIZE,
+       .init           =       sha256_init,
+       .update         =       sha256_update,
+       .final          =       sha256_final,
+       .descsize       =       sizeof(struct sha256_ctx),
+       .base           =       {
+               .cra_name       =       "sha256",
+               .cra_driver_name=       "sha256-generic",
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA256_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
-static struct crypto_alg sha224 = {
-       .cra_name       = "sha224",
-       .cra_driver_name = "sha224-generic",
-       .cra_flags      = CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize  = SHA224_BLOCK_SIZE,
-       .cra_ctxsize    = sizeof(struct sha256_ctx),
-       .cra_module     = THIS_MODULE,
-       .cra_alignmask  = 3,
-       .cra_list       = LIST_HEAD_INIT(sha224.cra_list),
-       .cra_u          = { .digest = {
-       .dia_digestsize = SHA224_DIGEST_SIZE,
-       .dia_init       = sha224_init,
-       .dia_update     = sha256_update,
-       .dia_final      = sha224_final } }
+static struct shash_alg sha224 = {
+       .digestsize     =       SHA224_DIGEST_SIZE,
+       .init           =       sha224_init,
+       .update         =       sha256_update,
+       .final          =       sha224_final,
+       .descsize       =       sizeof(struct sha256_ctx),
+       .base           =       {
+               .cra_name       =       "sha224",
+               .cra_driver_name=       "sha224-generic",
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA224_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
 static int __init sha256_generic_mod_init(void)
 {
        int ret = 0;
 
-       ret = crypto_register_alg(&sha224);
+       ret = crypto_register_shash(&sha224);
 
        if (ret < 0)
                return ret;
 
-       ret = crypto_register_alg(&sha256);
+       ret = crypto_register_shash(&sha256);
 
        if (ret < 0)
-               crypto_unregister_alg(&sha224);
+               crypto_unregister_shash(&sha224);
 
        return ret;
 }
 
 static void __exit sha256_generic_mod_fini(void)
 {
-       crypto_unregister_alg(&sha224);
-       crypto_unregister_alg(&sha256);
+       crypto_unregister_shash(&sha224);
+       crypto_unregister_shash(&sha256);
 }
 
 module_init(sha256_generic_mod_init);
index bc36861..3bea38d 100644 (file)
@@ -10,7 +10,7 @@
  * later version.
  *
  */
-
+#include <crypto/internal/hash.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mm.h>
 #include <linux/crypto.h>
 #include <linux/types.h>
 #include <crypto/sha.h>
-
+#include <linux/percpu.h>
 #include <asm/byteorder.h>
 
 struct sha512_ctx {
        u64 state[8];
        u32 count[4];
        u8 buf[128];
-       u64 W[80];
 };
 
+static DEFINE_PER_CPU(u64[80], msg_schedule);
+
 static inline u64 Ch(u64 x, u64 y, u64 z)
 {
         return z ^ (x & (y ^ z));
@@ -89,11 +90,12 @@ static inline void BLEND_OP(int I, u64 *W)
 }
 
 static void
-sha512_transform(u64 *state, u64 *W, const u8 *input)
+sha512_transform(u64 *state, const u8 *input)
 {
        u64 a, b, c, d, e, f, g, h, t1, t2;
 
        int i;
+       u64 *W = get_cpu_var(msg_schedule);
 
        /* load the input */
         for (i = 0; i < 16; i++)
@@ -132,12 +134,14 @@ sha512_transform(u64 *state, u64 *W, const u8 *input)
 
        /* erase our data */
        a = b = c = d = e = f = g = h = t1 = t2 = 0;
+       memset(W, 0, sizeof(__get_cpu_var(msg_schedule)));
+       put_cpu_var(msg_schedule);
 }
 
-static void
-sha512_init(struct crypto_tfm *tfm)
+static int
+sha512_init(struct shash_desc *desc)
 {
-       struct sha512_ctx *sctx = crypto_tfm_ctx(tfm);
+       struct sha512_ctx *sctx = shash_desc_ctx(desc);
        sctx->state[0] = SHA512_H0;
        sctx->state[1] = SHA512_H1;
        sctx->state[2] = SHA512_H2;
@@ -147,12 +151,14 @@ sha512_init(struct crypto_tfm *tfm)
        sctx->state[6] = SHA512_H6;
        sctx->state[7] = SHA512_H7;
        sctx->count[0] = sctx->count[1] = sctx->count[2] = sctx->count[3] = 0;
+
+       return 0;
 }
 
-static void
-sha384_init(struct crypto_tfm *tfm)
+static int
+sha384_init(struct shash_desc *desc)
 {
-       struct sha512_ctx *sctx = crypto_tfm_ctx(tfm);
+       struct sha512_ctx *sctx = shash_desc_ctx(desc);
        sctx->state[0] = SHA384_H0;
        sctx->state[1] = SHA384_H1;
        sctx->state[2] = SHA384_H2;
@@ -162,12 +168,14 @@ sha384_init(struct crypto_tfm *tfm)
        sctx->state[6] = SHA384_H6;
        sctx->state[7] = SHA384_H7;
         sctx->count[0] = sctx->count[1] = sctx->count[2] = sctx->count[3] = 0;
+
+       return 0;
 }
 
-static void
-sha512_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
+static int
+sha512_update(struct shash_desc *desc, const u8 *data, unsigned int len)
 {
-       struct sha512_ctx *sctx = crypto_tfm_ctx(tfm);
+       struct sha512_ctx *sctx = shash_desc_ctx(desc);
 
        unsigned int i, index, part_len;
 
@@ -187,10 +195,10 @@ sha512_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
        /* Transform as many times as possible. */
        if (len >= part_len) {
                memcpy(&sctx->buf[index], data, part_len);
-               sha512_transform(sctx->state, sctx->W, sctx->buf);
+               sha512_transform(sctx->state, sctx->buf);
 
                for (i = part_len; i + 127 < len; i+=128)
-                       sha512_transform(sctx->state, sctx->W, &data[i]);
+                       sha512_transform(sctx->state, &data[i]);
 
                index = 0;
        } else {
@@ -200,14 +208,13 @@ sha512_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
        /* Buffer remaining input */
        memcpy(&sctx->buf[index], &data[i], len - i);
 
-       /* erase our data */
-       memset(sctx->W, 0, sizeof(sctx->W));
+       return 0;
 }
 
-static void
-sha512_final(struct crypto_tfm *tfm, u8 *hash)
+static int
+sha512_final(struct shash_desc *desc, u8 *hash)
 {
-       struct sha512_ctx *sctx = crypto_tfm_ctx(tfm);
+       struct sha512_ctx *sctx = shash_desc_ctx(desc);
         static u8 padding[128] = { 0x80, };
        __be64 *dst = (__be64 *)hash;
        __be32 bits[4];
@@ -223,10 +230,10 @@ sha512_final(struct crypto_tfm *tfm, u8 *hash)
        /* Pad out to 112 mod 128. */
        index = (sctx->count[0] >> 3) & 0x7f;
        pad_len = (index < 112) ? (112 - index) : ((128+112) - index);
-       sha512_update(tfm, padding, pad_len);
+       sha512_update(desc, padding, pad_len);
 
        /* Append length (before padding) */
-       sha512_update(tfm, (const u8 *)bits, sizeof(bits));
+       sha512_update(desc, (const u8 *)bits, sizeof(bits));
 
        /* Store state in digest */
        for (i = 0; i < 8; i++)
@@ -234,66 +241,66 @@ sha512_final(struct crypto_tfm *tfm, u8 *hash)
 
        /* Zeroize sensitive information. */
        memset(sctx, 0, sizeof(struct sha512_ctx));
+
+       return 0;
 }
 
-static void sha384_final(struct crypto_tfm *tfm, u8 *hash)
+static int sha384_final(struct shash_desc *desc, u8 *hash)
 {
-        u8 D[64];
+       u8 D[64];
+
+       sha512_final(desc, D);
 
-       sha512_final(tfm, D);
+       memcpy(hash, D, 48);
+       memset(D, 0, 64);
 
-        memcpy(hash, D, 48);
-        memset(D, 0, 64);
+       return 0;
 }
 
-static struct crypto_alg sha512 = {
-        .cra_name       = "sha512",
-        .cra_flags      = CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize  = SHA512_BLOCK_SIZE,
-        .cra_ctxsize    = sizeof(struct sha512_ctx),
-        .cra_module     = THIS_MODULE,
-       .cra_alignmask  = 3,
-        .cra_list       = LIST_HEAD_INIT(sha512.cra_list),
-        .cra_u          = { .digest = {
-                                .dia_digestsize = SHA512_DIGEST_SIZE,
-                                .dia_init       = sha512_init,
-                                .dia_update     = sha512_update,
-                                .dia_final      = sha512_final }
-        }
+static struct shash_alg sha512 = {
+       .digestsize     =       SHA512_DIGEST_SIZE,
+       .init           =       sha512_init,
+       .update         =       sha512_update,
+       .final          =       sha512_final,
+       .descsize       =       sizeof(struct sha512_ctx),
+       .base           =       {
+               .cra_name       =       "sha512",
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA512_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
-static struct crypto_alg sha384 = {
-        .cra_name       = "sha384",
-        .cra_flags      = CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize  = SHA384_BLOCK_SIZE,
-        .cra_ctxsize    = sizeof(struct sha512_ctx),
-       .cra_alignmask  = 3,
-        .cra_module     = THIS_MODULE,
-        .cra_list       = LIST_HEAD_INIT(sha384.cra_list),
-        .cra_u          = { .digest = {
-                                .dia_digestsize = SHA384_DIGEST_SIZE,
-                                .dia_init       = sha384_init,
-                                .dia_update     = sha512_update,
-                                .dia_final      = sha384_final }
-        }
+static struct shash_alg sha384 = {
+       .digestsize     =       SHA384_DIGEST_SIZE,
+       .init           =       sha384_init,
+       .update         =       sha512_update,
+       .final          =       sha384_final,
+       .descsize       =       sizeof(struct sha512_ctx),
+       .base           =       {
+               .cra_name       =       "sha384",
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       SHA384_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
 static int __init sha512_generic_mod_init(void)
 {
         int ret = 0;
 
-        if ((ret = crypto_register_alg(&sha384)) < 0)
+        if ((ret = crypto_register_shash(&sha384)) < 0)
                 goto out;
-        if ((ret = crypto_register_alg(&sha512)) < 0)
-                crypto_unregister_alg(&sha384);
+        if ((ret = crypto_register_shash(&sha512)) < 0)
+                crypto_unregister_shash(&sha384);
 out:
         return ret;
 }
 
 static void __exit sha512_generic_mod_fini(void)
 {
-        crypto_unregister_alg(&sha384);
-        crypto_unregister_alg(&sha512);
+        crypto_unregister_shash(&sha384);
+        crypto_unregister_shash(&sha512);
 }
 
 module_init(sha512_generic_mod_init);
diff --git a/crypto/shash.c b/crypto/shash.c
new file mode 100644 (file)
index 0000000..c9df367
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+ * Synchronous Cryptographic Hash operations.
+ *
+ * Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+
+#include <crypto/scatterwalk.h>
+#include <crypto/internal/hash.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+
+static const struct crypto_type crypto_shash_type;
+
+static inline struct crypto_shash *__crypto_shash_cast(struct crypto_tfm *tfm)
+{
+       return container_of(tfm, struct crypto_shash, base);
+}
+
+#include "internal.h"
+
+static int shash_setkey_unaligned(struct crypto_shash *tfm, const u8 *key,
+                                 unsigned int keylen)
+{
+       struct shash_alg *shash = crypto_shash_alg(tfm);
+       unsigned long alignmask = crypto_shash_alignmask(tfm);
+       unsigned long absize;
+       u8 *buffer, *alignbuffer;
+       int err;
+
+       absize = keylen + (alignmask & ~(CRYPTO_MINALIGN - 1));
+       buffer = kmalloc(absize, GFP_KERNEL);
+       if (!buffer)
+               return -ENOMEM;
+
+       alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1);
+       memcpy(alignbuffer, key, keylen);
+       err = shash->setkey(tfm, alignbuffer, keylen);
+       memset(alignbuffer, 0, keylen);
+       kfree(buffer);
+       return err;
+}
+
+int crypto_shash_setkey(struct crypto_shash *tfm, const u8 *key,
+                       unsigned int keylen)
+{
+       struct shash_alg *shash = crypto_shash_alg(tfm);
+       unsigned long alignmask = crypto_shash_alignmask(tfm);
+
+       if (!shash->setkey)
+               return -ENOSYS;
+
+       if ((unsigned long)key & alignmask)
+               return shash_setkey_unaligned(tfm, key, keylen);
+
+       return shash->setkey(tfm, key, keylen);
+}
+EXPORT_SYMBOL_GPL(crypto_shash_setkey);
+
+static inline unsigned int shash_align_buffer_size(unsigned len,
+                                                  unsigned long mask)
+{
+       return len + (mask & ~(__alignof__(u8 __attribute__ ((aligned))) - 1));
+}
+
+static int shash_update_unaligned(struct shash_desc *desc, const u8 *data,
+                                 unsigned int len)
+{
+       struct crypto_shash *tfm = desc->tfm;
+       struct shash_alg *shash = crypto_shash_alg(tfm);
+       unsigned long alignmask = crypto_shash_alignmask(tfm);
+       unsigned int unaligned_len = alignmask + 1 -
+                                    ((unsigned long)data & alignmask);
+       u8 buf[shash_align_buffer_size(unaligned_len, alignmask)]
+               __attribute__ ((aligned));
+
+       memcpy(buf, data, unaligned_len);
+
+       return shash->update(desc, buf, unaligned_len) ?:
+              shash->update(desc, data + unaligned_len, len - unaligned_len);
+}
+
+int crypto_shash_update(struct shash_desc *desc, const u8 *data,
+                       unsigned int len)
+{
+       struct crypto_shash *tfm = desc->tfm;
+       struct shash_alg *shash = crypto_shash_alg(tfm);
+       unsigned long alignmask = crypto_shash_alignmask(tfm);
+
+       if ((unsigned long)data & alignmask)
+               return shash_update_unaligned(desc, data, len);
+
+       return shash->update(desc, data, len);
+}
+EXPORT_SYMBOL_GPL(crypto_shash_update);
+
+static int shash_final_unaligned(struct shash_desc *desc, u8 *out)
+{
+       struct crypto_shash *tfm = desc->tfm;
+       unsigned long alignmask = crypto_shash_alignmask(tfm);
+       struct shash_alg *shash = crypto_shash_alg(tfm);
+       unsigned int ds = crypto_shash_digestsize(tfm);
+       u8 buf[shash_align_buffer_size(ds, alignmask)]
+               __attribute__ ((aligned));
+       int err;
+
+       err = shash->final(desc, buf);
+       memcpy(out, buf, ds);
+       return err;
+}
+
+int crypto_shash_final(struct shash_desc *desc, u8 *out)
+{
+       struct crypto_shash *tfm = desc->tfm;
+       struct shash_alg *shash = crypto_shash_alg(tfm);
+       unsigned long alignmask = crypto_shash_alignmask(tfm);
+
+       if ((unsigned long)out & alignmask)
+               return shash_final_unaligned(desc, out);
+
+       return shash->final(desc, out);
+}
+EXPORT_SYMBOL_GPL(crypto_shash_final);
+
+static int shash_finup_unaligned(struct shash_desc *desc, const u8 *data,
+                                unsigned int len, u8 *out)
+{
+       return crypto_shash_update(desc, data, len) ?:
+              crypto_shash_final(desc, out);
+}
+
+int crypto_shash_finup(struct shash_desc *desc, const u8 *data,
+                      unsigned int len, u8 *out)
+{
+       struct crypto_shash *tfm = desc->tfm;
+       struct shash_alg *shash = crypto_shash_alg(tfm);
+       unsigned long alignmask = crypto_shash_alignmask(tfm);
+
+       if (((unsigned long)data | (unsigned long)out) & alignmask ||
+           !shash->finup)
+               return shash_finup_unaligned(desc, data, len, out);
+
+       return shash->finup(desc, data, len, out);
+}
+EXPORT_SYMBOL_GPL(crypto_shash_finup);
+
+static int shash_digest_unaligned(struct shash_desc *desc, const u8 *data,
+                                 unsigned int len, u8 *out)
+{
+       return crypto_shash_init(desc) ?:
+              crypto_shash_update(desc, data, len) ?:
+              crypto_shash_final(desc, out);
+}
+
+int crypto_shash_digest(struct shash_desc *desc, const u8 *data,
+                       unsigned int len, u8 *out)
+{
+       struct crypto_shash *tfm = desc->tfm;
+       struct shash_alg *shash = crypto_shash_alg(tfm);
+       unsigned long alignmask = crypto_shash_alignmask(tfm);
+
+       if (((unsigned long)data | (unsigned long)out) & alignmask ||
+           !shash->digest)
+               return shash_digest_unaligned(desc, data, len, out);
+
+       return shash->digest(desc, data, len, out);
+}
+EXPORT_SYMBOL_GPL(crypto_shash_digest);
+
+int crypto_shash_import(struct shash_desc *desc, const u8 *in)
+{
+       struct crypto_shash *tfm = desc->tfm;
+       struct shash_alg *alg = crypto_shash_alg(tfm);
+
+       memcpy(shash_desc_ctx(desc), in, crypto_shash_descsize(tfm));
+
+       if (alg->reinit)
+               alg->reinit(desc);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(crypto_shash_import);
+
+static int shash_async_setkey(struct crypto_ahash *tfm, const u8 *key,
+                             unsigned int keylen)
+{
+       struct crypto_shash **ctx = crypto_ahash_ctx(tfm);
+
+       return crypto_shash_setkey(*ctx, key, keylen);
+}
+
+static int shash_async_init(struct ahash_request *req)
+{
+       struct crypto_shash **ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
+       struct shash_desc *desc = ahash_request_ctx(req);
+
+       desc->tfm = *ctx;
+       desc->flags = req->base.flags;
+
+       return crypto_shash_init(desc);
+}
+
+static int shash_async_update(struct ahash_request *req)
+{
+       struct shash_desc *desc = ahash_request_ctx(req);
+       struct crypto_hash_walk walk;
+       int nbytes;
+
+       for (nbytes = crypto_hash_walk_first(req, &walk); nbytes > 0;
+            nbytes = crypto_hash_walk_done(&walk, nbytes))
+               nbytes = crypto_shash_update(desc, walk.data, nbytes);
+
+       return nbytes;
+}
+
+static int shash_async_final(struct ahash_request *req)
+{
+       return crypto_shash_final(ahash_request_ctx(req), req->result);
+}
+
+static int shash_async_digest(struct ahash_request *req)
+{
+       struct scatterlist *sg = req->src;
+       unsigned int offset = sg->offset;
+       unsigned int nbytes = req->nbytes;
+       int err;
+
+       if (nbytes < min(sg->length, ((unsigned int)(PAGE_SIZE)) - offset)) {
+               struct crypto_shash **ctx =
+                       crypto_ahash_ctx(crypto_ahash_reqtfm(req));
+               struct shash_desc *desc = ahash_request_ctx(req);
+               void *data;
+
+               desc->tfm = *ctx;
+               desc->flags = req->base.flags;
+
+               data = crypto_kmap(sg_page(sg), 0);
+               err = crypto_shash_digest(desc, data + offset, nbytes,
+                                         req->result);
+               crypto_kunmap(data, 0);
+               crypto_yield(desc->flags);
+               goto out;
+       }
+
+       err = shash_async_init(req);
+       if (err)
+               goto out;
+
+       err = shash_async_update(req);
+       if (err)
+               goto out;
+
+       err = shash_async_final(req);
+
+out:
+       return err;
+}
+
+static void crypto_exit_shash_ops_async(struct crypto_tfm *tfm)
+{
+       struct crypto_shash **ctx = crypto_tfm_ctx(tfm);
+
+       crypto_free_shash(*ctx);
+}
+
+static int crypto_init_shash_ops_async(struct crypto_tfm *tfm)
+{
+       struct crypto_alg *calg = tfm->__crt_alg;
+       struct shash_alg *alg = __crypto_shash_alg(calg);
+       struct ahash_tfm *crt = &tfm->crt_ahash;
+       struct crypto_shash **ctx = crypto_tfm_ctx(tfm);
+       struct crypto_shash *shash;
+
+       if (!crypto_mod_get(calg))
+               return -EAGAIN;
+
+       shash = __crypto_shash_cast(crypto_create_tfm(
+               calg, &crypto_shash_type));
+       if (IS_ERR(shash)) {
+               crypto_mod_put(calg);
+               return PTR_ERR(shash);
+       }
+
+       *ctx = shash;
+       tfm->exit = crypto_exit_shash_ops_async;
+
+       crt->init = shash_async_init;
+       crt->update = shash_async_update;
+       crt->final  = shash_async_final;
+       crt->digest = shash_async_digest;
+       crt->setkey = shash_async_setkey;
+
+       crt->digestsize = alg->digestsize;
+       crt->reqsize = sizeof(struct shash_desc) + crypto_shash_descsize(shash);
+
+       return 0;
+}
+
+static int shash_compat_setkey(struct crypto_hash *tfm, const u8 *key,
+                              unsigned int keylen)
+{
+       struct shash_desc *desc = crypto_hash_ctx(tfm);
+
+       return crypto_shash_setkey(desc->tfm, key, keylen);
+}
+
+static int shash_compat_init(struct hash_desc *hdesc)
+{
+       struct shash_desc *desc = crypto_hash_ctx(hdesc->tfm);
+
+       desc->flags = hdesc->flags;
+
+       return crypto_shash_init(desc);
+}
+
+static int shash_compat_update(struct hash_desc *hdesc, struct scatterlist *sg,
+                              unsigned int len)
+{
+       struct shash_desc *desc = crypto_hash_ctx(hdesc->tfm);
+       struct crypto_hash_walk walk;
+       int nbytes;
+
+       for (nbytes = crypto_hash_walk_first_compat(hdesc, &walk, sg, len);
+            nbytes > 0; nbytes = crypto_hash_walk_done(&walk, nbytes))
+               nbytes = crypto_shash_update(desc, walk.data, nbytes);
+
+       return nbytes;
+}
+
+static int shash_compat_final(struct hash_desc *hdesc, u8 *out)
+{
+       return crypto_shash_final(crypto_hash_ctx(hdesc->tfm), out);
+}
+
+static int shash_compat_digest(struct hash_desc *hdesc, struct scatterlist *sg,
+                              unsigned int nbytes, u8 *out)
+{
+       unsigned int offset = sg->offset;
+       int err;
+
+       if (nbytes < min(sg->length, ((unsigned int)(PAGE_SIZE)) - offset)) {
+               struct shash_desc *desc = crypto_hash_ctx(hdesc->tfm);
+               void *data;
+
+               desc->flags = hdesc->flags;
+
+               data = crypto_kmap(sg_page(sg), 0);
+               err = crypto_shash_digest(desc, data + offset, nbytes, out);
+               crypto_kunmap(data, 0);
+               crypto_yield(desc->flags);
+               goto out;
+       }
+
+       err = shash_compat_init(hdesc);
+       if (err)
+               goto out;
+
+       err = shash_compat_update(hdesc, sg, nbytes);
+       if (err)
+               goto out;
+
+       err = shash_compat_final(hdesc, out);
+
+out:
+       return err;
+}
+
+static void crypto_exit_shash_ops_compat(struct crypto_tfm *tfm)
+{
+       struct shash_desc *desc= crypto_tfm_ctx(tfm);
+
+       crypto_free_shash(desc->tfm);
+}
+
+static int crypto_init_shash_ops_compat(struct crypto_tfm *tfm)
+{
+       struct hash_tfm *crt = &tfm->crt_hash;
+       struct crypto_alg *calg = tfm->__crt_alg;
+       struct shash_alg *alg = __crypto_shash_alg(calg);
+       struct shash_desc *desc = crypto_tfm_ctx(tfm);
+       struct crypto_shash *shash;
+
+       shash = __crypto_shash_cast(crypto_create_tfm(
+               calg, &crypto_shash_type));
+       if (IS_ERR(shash))
+               return PTR_ERR(shash);
+
+       desc->tfm = shash;
+       tfm->exit = crypto_exit_shash_ops_compat;
+
+       crt->init = shash_compat_init;
+       crt->update = shash_compat_update;
+       crt->final  = shash_compat_final;
+       crt->digest = shash_compat_digest;
+       crt->setkey = shash_compat_setkey;
+
+       crt->digestsize = alg->digestsize;
+
+       return 0;
+}
+
+static int crypto_init_shash_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
+{
+       switch (mask & CRYPTO_ALG_TYPE_MASK) {
+       case CRYPTO_ALG_TYPE_HASH_MASK:
+               return crypto_init_shash_ops_compat(tfm);
+       case CRYPTO_ALG_TYPE_AHASH_MASK:
+               return crypto_init_shash_ops_async(tfm);
+       }
+
+       return -EINVAL;
+}
+
+static unsigned int crypto_shash_ctxsize(struct crypto_alg *alg, u32 type,
+                                        u32 mask)
+{
+       struct shash_alg *salg = __crypto_shash_alg(alg);
+
+       switch (mask & CRYPTO_ALG_TYPE_MASK) {
+       case CRYPTO_ALG_TYPE_HASH_MASK:
+               return sizeof(struct shash_desc) + salg->descsize;
+       case CRYPTO_ALG_TYPE_AHASH_MASK:
+               return sizeof(struct crypto_shash *);
+       }
+
+       return 0;
+}
+
+static int crypto_shash_init_tfm(struct crypto_tfm *tfm,
+                                const struct crypto_type *frontend)
+{
+       if (frontend->type != CRYPTO_ALG_TYPE_SHASH)
+               return -EINVAL;
+       return 0;
+}
+
+static unsigned int crypto_shash_extsize(struct crypto_alg *alg,
+                                        const struct crypto_type *frontend)
+{
+       return alg->cra_ctxsize;
+}
+
+static void crypto_shash_show(struct seq_file *m, struct crypto_alg *alg)
+       __attribute__ ((unused));
+static void crypto_shash_show(struct seq_file *m, struct crypto_alg *alg)
+{
+       struct shash_alg *salg = __crypto_shash_alg(alg);
+
+       seq_printf(m, "type         : shash\n");
+       seq_printf(m, "blocksize    : %u\n", alg->cra_blocksize);
+       seq_printf(m, "digestsize   : %u\n", salg->digestsize);
+       seq_printf(m, "descsize     : %u\n", salg->descsize);
+}
+
+static const struct crypto_type crypto_shash_type = {
+       .ctxsize = crypto_shash_ctxsize,
+       .extsize = crypto_shash_extsize,
+       .init = crypto_init_shash_ops,
+       .init_tfm = crypto_shash_init_tfm,
+#ifdef CONFIG_PROC_FS
+       .show = crypto_shash_show,
+#endif
+       .maskclear = ~CRYPTO_ALG_TYPE_MASK,
+       .maskset = CRYPTO_ALG_TYPE_MASK,
+       .type = CRYPTO_ALG_TYPE_SHASH,
+       .tfmsize = offsetof(struct crypto_shash, base),
+};
+
+struct crypto_shash *crypto_alloc_shash(const char *alg_name, u32 type,
+                                       u32 mask)
+{
+       return __crypto_shash_cast(
+               crypto_alloc_tfm(alg_name, &crypto_shash_type, type, mask));
+}
+EXPORT_SYMBOL_GPL(crypto_alloc_shash);
+
+int crypto_register_shash(struct shash_alg *alg)
+{
+       struct crypto_alg *base = &alg->base;
+
+       if (alg->digestsize > PAGE_SIZE / 8 ||
+           alg->descsize > PAGE_SIZE / 8)
+               return -EINVAL;
+
+       base->cra_type = &crypto_shash_type;
+       base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
+       base->cra_flags |= CRYPTO_ALG_TYPE_SHASH;
+
+       return crypto_register_alg(base);
+}
+EXPORT_SYMBOL_GPL(crypto_register_shash);
+
+int crypto_unregister_shash(struct shash_alg *alg)
+{
+       return crypto_unregister_alg(&alg->base);
+}
+EXPORT_SYMBOL_GPL(crypto_unregister_shash);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Synchronous cryptographic hash type");
index b828c6c..a75f11f 100644 (file)
@@ -843,6 +843,14 @@ static int test_comp(struct crypto_comp *tfm, struct comp_testvec *ctemplate,
                        goto out;
                }
 
+               if (dlen != ctemplate[i].outlen) {
+                       printk(KERN_ERR "alg: comp: Compression test %d "
+                              "failed for %s: output len = %d\n", i + 1, algo,
+                              dlen);
+                       ret = -EINVAL;
+                       goto out;
+               }
+
                if (memcmp(result, ctemplate[i].output, dlen)) {
                        printk(KERN_ERR "alg: comp: Compression test %d "
                               "failed for %s\n", i + 1, algo);
@@ -853,7 +861,7 @@ static int test_comp(struct crypto_comp *tfm, struct comp_testvec *ctemplate,
        }
 
        for (i = 0; i < dtcount; i++) {
-               int ilen, ret, dlen = COMP_BUF_SIZE;
+               int ilen, dlen = COMP_BUF_SIZE;
 
                memset(result, 0, sizeof (result));
 
@@ -867,6 +875,14 @@ static int test_comp(struct crypto_comp *tfm, struct comp_testvec *ctemplate,
                        goto out;
                }
 
+               if (dlen != dtemplate[i].outlen) {
+                       printk(KERN_ERR "alg: comp: Decompression test %d "
+                              "failed for %s: output len = %d\n", i + 1, algo,
+                              dlen);
+                       ret = -EINVAL;
+                       goto out;
+               }
+
                if (memcmp(result, dtemplate[i].output, dlen)) {
                        printk(KERN_ERR "alg: comp: Decompression test %d "
                               "failed for %s\n", i + 1, algo);
@@ -1010,6 +1026,55 @@ static int alg_test_hash(const struct alg_test_desc *desc, const char *driver,
        return err;
 }
 
+static int alg_test_crc32c(const struct alg_test_desc *desc,
+                          const char *driver, u32 type, u32 mask)
+{
+       struct crypto_shash *tfm;
+       u32 val;
+       int err;
+
+       err = alg_test_hash(desc, driver, type, mask);
+       if (err)
+               goto out;
+
+       tfm = crypto_alloc_shash(driver, type, mask);
+       if (IS_ERR(tfm)) {
+               printk(KERN_ERR "alg: crc32c: Failed to load transform for %s: "
+                      "%ld\n", driver, PTR_ERR(tfm));
+               err = PTR_ERR(tfm);
+               goto out;
+       }
+
+       do {
+               struct {
+                       struct shash_desc shash;
+                       char ctx[crypto_shash_descsize(tfm)];
+               } sdesc;
+
+               sdesc.shash.tfm = tfm;
+               sdesc.shash.flags = 0;
+
+               *(u32 *)sdesc.ctx = le32_to_cpu(420553207);
+               err = crypto_shash_final(&sdesc.shash, (u8 *)&val);
+               if (err) {
+                       printk(KERN_ERR "alg: crc32c: Operation failed for "
+                              "%s: %d\n", driver, err);
+                       break;
+               }
+
+               if (val != ~420553207) {
+                       printk(KERN_ERR "alg: crc32c: Test failed for %s: "
+                              "%d\n", driver, val);
+                       err = -EINVAL;
+               }
+       } while (0);
+
+       crypto_free_shash(tfm);
+
+out:
+       return err;
+}
+
 /* Please keep this list sorted by algorithm name. */
 static const struct alg_test_desc alg_test_descs[] = {
        {
@@ -1134,7 +1199,7 @@ static const struct alg_test_desc alg_test_descs[] = {
                }
        }, {
                .alg = "crc32c",
-               .test = alg_test_hash,
+               .test = alg_test_crc32c,
                .suite = {
                        .hash = {
                                .vecs = crc32c_tv_template,
@@ -1801,6 +1866,7 @@ static int alg_find_test(const char *alg)
 int alg_test(const char *driver, const char *alg, u32 type, u32 mask)
 {
        int i;
+       int rc;
 
        if ((type & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_CIPHER) {
                char nalg[CRYPTO_MAX_ALG_NAME];
@@ -1820,8 +1886,12 @@ int alg_test(const char *driver, const char *alg, u32 type, u32 mask)
        if (i < 0)
                goto notest;
 
-       return alg_test_descs[i].test(alg_test_descs + i, driver,
+       rc = alg_test_descs[i].test(alg_test_descs + i, driver,
                                      type, mask);
+       if (fips_enabled && rc)
+               panic("%s: %s alg self test failed in fips mode!\n", driver, alg);
+
+       return rc;
 
 notest:
        printk(KERN_INFO "alg: No test for %s (%s)\n", alg, driver);
index dee94d9..132953e 100644 (file)
@@ -8349,7 +8349,7 @@ struct comp_testvec {
 
 /*
  * Deflate test vectors (null-terminated strings).
- * Params: winbits=11, Z_DEFAULT_COMPRESSION, MAX_MEM_LEVEL.
+ * Params: winbits=-11, Z_DEFAULT_COMPRESSION, MAX_MEM_LEVEL.
  */
 #define DEFLATE_COMP_TEST_VECTORS 2
 #define DEFLATE_DECOMP_TEST_VECTORS 2
index a92414f..cbca4f2 100644 (file)
  * (at your option) any later version.
  *
  */
+#include <crypto/internal/hash.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
 #include <asm/byteorder.h>
-#include <linux/crypto.h>
 #include <linux/types.h>
 
 #define TGR192_DIGEST_SIZE 24
@@ -495,24 +495,26 @@ static void tgr192_transform(struct tgr192_ctx *tctx, const u8 * data)
        tctx->c = c;
 }
 
-static void tgr192_init(struct crypto_tfm *tfm)
+static int tgr192_init(struct shash_desc *desc)
 {
-       struct tgr192_ctx *tctx = crypto_tfm_ctx(tfm);
+       struct tgr192_ctx *tctx = shash_desc_ctx(desc);
 
        tctx->a = 0x0123456789abcdefULL;
        tctx->b = 0xfedcba9876543210ULL;
        tctx->c = 0xf096a5b4c3b2e187ULL;
        tctx->nblocks = 0;
        tctx->count = 0;
+
+       return 0;
 }
 
 
 /* Update the message digest with the contents
  * of INBUF with length INLEN. */
-static void tgr192_update(struct crypto_tfm *tfm, const u8 *inbuf,
+static int tgr192_update(struct shash_desc *desc, const u8 *inbuf,
                          unsigned int len)
 {
-       struct tgr192_ctx *tctx = crypto_tfm_ctx(tfm);
+       struct tgr192_ctx *tctx = shash_desc_ctx(desc);
 
        if (tctx->count == 64) {        /* flush the buffer */
                tgr192_transform(tctx, tctx->hash);
@@ -520,15 +522,15 @@ static void tgr192_update(struct crypto_tfm *tfm, const u8 *inbuf,
                tctx->nblocks++;
        }
        if (!inbuf) {
-               return;
+               return 0;
        }
        if (tctx->count) {
                for (; len && tctx->count < 64; len--) {
                        tctx->hash[tctx->count++] = *inbuf++;
                }
-               tgr192_update(tfm, NULL, 0);
+               tgr192_update(desc, NULL, 0);
                if (!len) {
-                       return;
+                       return 0;
                }
 
        }
@@ -543,20 +545,22 @@ static void tgr192_update(struct crypto_tfm *tfm, const u8 *inbuf,
        for (; len && tctx->count < 64; len--) {
                tctx->hash[tctx->count++] = *inbuf++;
        }
+
+       return 0;
 }
 
 
 
 /* The routine terminates the computation */
-static void tgr192_final(struct crypto_tfm *tfm, u8 * out)
+static int tgr192_final(struct shash_desc *desc, u8 * out)
 {
-       struct tgr192_ctx *tctx = crypto_tfm_ctx(tfm);
+       struct tgr192_ctx *tctx = shash_desc_ctx(desc);
        __be64 *dst = (__be64 *)out;
        __be64 *be64p;
        __le32 *le32p;
        u32 t, msb, lsb;
 
-       tgr192_update(tfm, NULL, 0); /* flush */ ;
+       tgr192_update(desc, NULL, 0); /* flush */ ;
 
        msb = 0;
        t = tctx->nblocks;
@@ -584,7 +588,7 @@ static void tgr192_final(struct crypto_tfm *tfm, u8 * out)
                while (tctx->count < 64) {
                        tctx->hash[tctx->count++] = 0;
                }
-               tgr192_update(tfm, NULL, 0); /* flush */ ;
+               tgr192_update(desc, NULL, 0); /* flush */ ;
                memset(tctx->hash, 0, 56);    /* fill next block with zeroes */
        }
        /* append the 64 bit count */
@@ -598,91 +602,94 @@ static void tgr192_final(struct crypto_tfm *tfm, u8 * out)
        dst[0] = be64p[0] = cpu_to_be64(tctx->a);
        dst[1] = be64p[1] = cpu_to_be64(tctx->b);
        dst[2] = be64p[2] = cpu_to_be64(tctx->c);
+
+       return 0;
 }
 
-static void tgr160_final(struct crypto_tfm *tfm, u8 * out)
+static int tgr160_final(struct shash_desc *desc, u8 * out)
 {
        u8 D[64];
 
-       tgr192_final(tfm, D);
+       tgr192_final(desc, D);
        memcpy(out, D, TGR160_DIGEST_SIZE);
        memset(D, 0, TGR192_DIGEST_SIZE);
+
+       return 0;
 }
 
-static void tgr128_final(struct crypto_tfm *tfm, u8 * out)
+static int tgr128_final(struct shash_desc *desc, u8 * out)
 {
        u8 D[64];
 
-       tgr192_final(tfm, D);
+       tgr192_final(desc, D);
        memcpy(out, D, TGR128_DIGEST_SIZE);
        memset(D, 0, TGR192_DIGEST_SIZE);
+
+       return 0;
 }
 
-static struct crypto_alg tgr192 = {
-       .cra_name = "tgr192",
-       .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize = TGR192_BLOCK_SIZE,
-       .cra_ctxsize = sizeof(struct tgr192_ctx),
-       .cra_module = THIS_MODULE,
-       .cra_alignmask = 7,
-       .cra_list = LIST_HEAD_INIT(tgr192.cra_list),
-       .cra_u = {.digest = {
-                            .dia_digestsize = TGR192_DIGEST_SIZE,
-                            .dia_init = tgr192_init,
-                            .dia_update = tgr192_update,
-                            .dia_final = tgr192_final}}
+static struct shash_alg tgr192 = {
+       .digestsize     =       TGR192_DIGEST_SIZE,
+       .init           =       tgr192_init,
+       .update         =       tgr192_update,
+       .final          =       tgr192_final,
+       .descsize       =       sizeof(struct tgr192_ctx),
+       .base           =       {
+               .cra_name       =       "tgr192",
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       TGR192_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
-static struct crypto_alg tgr160 = {
-       .cra_name = "tgr160",
-       .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize = TGR192_BLOCK_SIZE,
-       .cra_ctxsize = sizeof(struct tgr192_ctx),
-       .cra_module = THIS_MODULE,
-       .cra_alignmask = 7,
-       .cra_list = LIST_HEAD_INIT(tgr160.cra_list),
-       .cra_u = {.digest = {
-                            .dia_digestsize = TGR160_DIGEST_SIZE,
-                            .dia_init = tgr192_init,
-                            .dia_update = tgr192_update,
-                            .dia_final = tgr160_final}}
+static struct shash_alg tgr160 = {
+       .digestsize     =       TGR160_DIGEST_SIZE,
+       .init           =       tgr192_init,
+       .update         =       tgr192_update,
+       .final          =       tgr160_final,
+       .descsize       =       sizeof(struct tgr192_ctx),
+       .base           =       {
+               .cra_name       =       "tgr160",
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       TGR192_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
-static struct crypto_alg tgr128 = {
-       .cra_name = "tgr128",
-       .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize = TGR192_BLOCK_SIZE,
-       .cra_ctxsize = sizeof(struct tgr192_ctx),
-       .cra_module = THIS_MODULE,
-       .cra_alignmask = 7,
-       .cra_list = LIST_HEAD_INIT(tgr128.cra_list),
-       .cra_u = {.digest = {
-                            .dia_digestsize = TGR128_DIGEST_SIZE,
-                            .dia_init = tgr192_init,
-                            .dia_update = tgr192_update,
-                            .dia_final = tgr128_final}}
+static struct shash_alg tgr128 = {
+       .digestsize     =       TGR128_DIGEST_SIZE,
+       .init           =       tgr192_init,
+       .update         =       tgr192_update,
+       .final          =       tgr128_final,
+       .descsize       =       sizeof(struct tgr192_ctx),
+       .base           =       {
+               .cra_name       =       "tgr128",
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       TGR192_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
 static int __init tgr192_mod_init(void)
 {
        int ret = 0;
 
-       ret = crypto_register_alg(&tgr192);
+       ret = crypto_register_shash(&tgr192);
 
        if (ret < 0) {
                goto out;
        }
 
-       ret = crypto_register_alg(&tgr160);
+       ret = crypto_register_shash(&tgr160);
        if (ret < 0) {
-               crypto_unregister_alg(&tgr192);
+               crypto_unregister_shash(&tgr192);
                goto out;
        }
 
-       ret = crypto_register_alg(&tgr128);
+       ret = crypto_register_shash(&tgr128);
        if (ret < 0) {
-               crypto_unregister_alg(&tgr192);
-               crypto_unregister_alg(&tgr160);
+               crypto_unregister_shash(&tgr192);
+               crypto_unregister_shash(&tgr160);
        }
       out:
        return ret;
@@ -690,9 +697,9 @@ static int __init tgr192_mod_init(void)
 
 static void __exit tgr192_mod_fini(void)
 {
-       crypto_unregister_alg(&tgr192);
-       crypto_unregister_alg(&tgr160);
-       crypto_unregister_alg(&tgr128);
+       crypto_unregister_shash(&tgr192);
+       crypto_unregister_shash(&tgr160);
+       crypto_unregister_shash(&tgr128);
 }
 
 MODULE_ALIAS("tgr160");
index bff2856..7234272 100644 (file)
  * (at your option) any later version.
  *
  */
+#include <crypto/internal/hash.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
 #include <asm/byteorder.h>
-#include <linux/crypto.h>
 #include <linux/types.h>
 
 #define WP512_DIGEST_SIZE 64
@@ -980,8 +980,8 @@ static void wp512_process_buffer(struct wp512_ctx *wctx) {
 
 }
 
-static void wp512_init(struct crypto_tfm *tfm) {
-       struct wp512_ctx *wctx = crypto_tfm_ctx(tfm);
+static int wp512_init(struct shash_desc *desc) {
+       struct wp512_ctx *wctx = shash_desc_ctx(desc);
        int i;
 
        memset(wctx->bitLength, 0, 32);
@@ -990,12 +990,14 @@ static void wp512_init(struct crypto_tfm *tfm) {
        for (i = 0; i < 8; i++) {
                wctx->hash[i] = 0L;
        }
+
+       return 0;
 }
 
-static void wp512_update(struct crypto_tfm *tfm, const u8 *source,
+static int wp512_update(struct shash_desc *desc, const u8 *source,
                         unsigned int len)
 {
-       struct wp512_ctx *wctx = crypto_tfm_ctx(tfm);
+       struct wp512_ctx *wctx = shash_desc_ctx(desc);
        int sourcePos    = 0;
        unsigned int bits_len = len * 8; // convert to number of bits
        int sourceGap    = (8 - ((int)bits_len & 7)) & 7;
@@ -1051,11 +1053,12 @@ static void wp512_update(struct crypto_tfm *tfm, const u8 *source,
        wctx->bufferBits   = bufferBits;
        wctx->bufferPos    = bufferPos;
 
+       return 0;
 }
 
-static void wp512_final(struct crypto_tfm *tfm, u8 *out)
+static int wp512_final(struct shash_desc *desc, u8 *out)
 {
-       struct wp512_ctx *wctx = crypto_tfm_ctx(tfm);
+       struct wp512_ctx *wctx = shash_desc_ctx(desc);
        int i;
        u8 *buffer      = wctx->buffer;
        u8 *bitLength   = wctx->bitLength;
@@ -1084,89 +1087,95 @@ static void wp512_final(struct crypto_tfm *tfm, u8 *out)
                digest[i] = cpu_to_be64(wctx->hash[i]);
        wctx->bufferBits   = bufferBits;
        wctx->bufferPos    = bufferPos;
+
+       return 0;
 }
 
-static void wp384_final(struct crypto_tfm *tfm, u8 *out)
+static int wp384_final(struct shash_desc *desc, u8 *out)
 {
        u8 D[64];
 
-       wp512_final(tfm, D);
+       wp512_final(desc, D);
        memcpy (out, D, WP384_DIGEST_SIZE);
        memset (D, 0, WP512_DIGEST_SIZE);
+
+       return 0;
 }
 
-static void wp256_final(struct crypto_tfm *tfm, u8 *out)
+static int wp256_final(struct shash_desc *desc, u8 *out)
 {
        u8 D[64];
 
-       wp512_final(tfm, D);
+       wp512_final(desc, D);
        memcpy (out, D, WP256_DIGEST_SIZE);
        memset (D, 0, WP512_DIGEST_SIZE);
+
+       return 0;
 }
 
-static struct crypto_alg wp512 = {
-       .cra_name       =       "wp512",
-       .cra_flags      =       CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize  =       WP512_BLOCK_SIZE,
-       .cra_ctxsize    =       sizeof(struct wp512_ctx),
-       .cra_module     =       THIS_MODULE,
-       .cra_list       =       LIST_HEAD_INIT(wp512.cra_list), 
-       .cra_u          =       { .digest = {
-       .dia_digestsize =       WP512_DIGEST_SIZE,
-       .dia_init       =       wp512_init,
-       .dia_update     =       wp512_update,
-       .dia_final      =       wp512_final } }
+static struct shash_alg wp512 = {
+       .digestsize     =       WP512_DIGEST_SIZE,
+       .init           =       wp512_init,
+       .update         =       wp512_update,
+       .final          =       wp512_final,
+       .descsize       =       sizeof(struct wp512_ctx),
+       .base           =       {
+               .cra_name       =       "wp512",
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       WP512_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
-static struct crypto_alg wp384 = {
-       .cra_name       =       "wp384",
-       .cra_flags      =       CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize  =       WP512_BLOCK_SIZE,
-       .cra_ctxsize    =       sizeof(struct wp512_ctx),
-       .cra_module     =       THIS_MODULE,
-       .cra_list       =       LIST_HEAD_INIT(wp384.cra_list), 
-       .cra_u          =       { .digest = {
-       .dia_digestsize =       WP384_DIGEST_SIZE,
-       .dia_init       =       wp512_init,
-       .dia_update     =       wp512_update,
-       .dia_final      =       wp384_final } }
+static struct shash_alg wp384 = {
+       .digestsize     =       WP384_DIGEST_SIZE,
+       .init           =       wp512_init,
+       .update         =       wp512_update,
+       .final          =       wp384_final,
+       .descsize       =       sizeof(struct wp512_ctx),
+       .base           =       {
+               .cra_name       =       "wp384",
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       WP512_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
-static struct crypto_alg wp256 = {
-       .cra_name       =       "wp256",
-       .cra_flags      =       CRYPTO_ALG_TYPE_DIGEST,
-       .cra_blocksize  =       WP512_BLOCK_SIZE,
-       .cra_ctxsize    =       sizeof(struct wp512_ctx),
-       .cra_module     =       THIS_MODULE,
-       .cra_list       =       LIST_HEAD_INIT(wp256.cra_list), 
-       .cra_u          =       { .digest = {
-       .dia_digestsize =       WP256_DIGEST_SIZE,
-       .dia_init       =       wp512_init,
-       .dia_update     =       wp512_update,
-       .dia_final      =       wp256_final } }
+static struct shash_alg wp256 = {
+       .digestsize     =       WP256_DIGEST_SIZE,
+       .init           =       wp512_init,
+       .update         =       wp512_update,
+       .final          =       wp256_final,
+       .descsize       =       sizeof(struct wp512_ctx),
+       .base           =       {
+               .cra_name       =       "wp256",
+               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize  =       WP512_BLOCK_SIZE,
+               .cra_module     =       THIS_MODULE,
+       }
 };
 
 static int __init wp512_mod_init(void)
 {
        int ret = 0;
 
-       ret = crypto_register_alg(&wp512);
+       ret = crypto_register_shash(&wp512);
 
        if (ret < 0)
                goto out;
 
-       ret = crypto_register_alg(&wp384);
+       ret = crypto_register_shash(&wp384);
        if (ret < 0)
        {
-               crypto_unregister_alg(&wp512);
+               crypto_unregister_shash(&wp512);
                goto out;
        }
 
-       ret = crypto_register_alg(&wp256);
+       ret = crypto_register_shash(&wp256);
        if (ret < 0)
        {
-               crypto_unregister_alg(&wp512);
-               crypto_unregister_alg(&wp384);
+               crypto_unregister_shash(&wp512);
+               crypto_unregister_shash(&wp384);
        }
 out:
        return ret;
@@ -1174,9 +1183,9 @@ out:
 
 static void __exit wp512_mod_fini(void)
 {
-       crypto_unregister_alg(&wp512);
-       crypto_unregister_alg(&wp384);
-       crypto_unregister_alg(&wp256);
+       crypto_unregister_shash(&wp512);
+       crypto_unregister_shash(&wp384);
+       crypto_unregister_shash(&wp256);
 }
 
 MODULE_ALIAS("wp384");
index 5c4ee70..fb06ed6 100644 (file)
@@ -936,8 +936,10 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
 {
        int err;
        struct loop_func_table *xfer;
+       uid_t uid = current_uid();
 
-       if (lo->lo_encrypt_key_size && lo->lo_key_owner != current->uid &&
+       if (lo->lo_encrypt_key_size &&
+           lo->lo_key_owner != uid &&
            !capable(CAP_SYS_ADMIN))
                return -EPERM;
        if (lo->lo_state != Lo_bound)
@@ -992,7 +994,7 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
        if (info->lo_encrypt_key_size) {
                memcpy(lo->lo_encrypt_key, info->lo_encrypt_key,
                       info->lo_encrypt_key_size);
-               lo->lo_key_owner = current->uid;
+               lo->lo_key_owner = uid;
        }       
 
        return 0;
index 5787249..34ab6d7 100644 (file)
@@ -67,6 +67,29 @@ static void tty_audit_buf_put(struct tty_audit_buf *buf)
                tty_audit_buf_free(buf);
 }
 
+static void tty_audit_log(const char *description, struct task_struct *tsk,
+                         uid_t loginuid, unsigned sessionid, int major,
+                         int minor, unsigned char *data, size_t size)
+{
+       struct audit_buffer *ab;
+
+       ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY);
+       if (ab) {
+               char name[sizeof(tsk->comm)];
+               uid_t uid = task_uid(tsk);
+
+               audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u "
+                                "major=%d minor=%d comm=", description,
+                                tsk->pid, uid, loginuid, sessionid,
+                                major, minor);
+               get_task_comm(name, tsk);
+               audit_log_untrustedstring(ab, name);
+               audit_log_format(ab, " data=");
+               audit_log_n_hex(ab, data, size);
+               audit_log_end(ab);
+       }
+}
+
 /**
  *     tty_audit_buf_push      -       Push buffered data out
  *
@@ -77,25 +100,12 @@ static void tty_audit_buf_push(struct task_struct *tsk, uid_t loginuid,
                               unsigned int sessionid,
                               struct tty_audit_buf *buf)
 {
-       struct audit_buffer *ab;
-
        if (buf->valid == 0)
                return;
        if (audit_enabled == 0)
                return;
-       ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY);
-       if (ab) {
-               char name[sizeof(tsk->comm)];
-
-               audit_log_format(ab, "tty pid=%u uid=%u auid=%u ses=%u "
-                                "major=%d minor=%d comm=", tsk->pid, tsk->uid,
-                                loginuid, sessionid, buf->major, buf->minor);
-               get_task_comm(name, tsk);
-               audit_log_untrustedstring(ab, name);
-               audit_log_format(ab, " data=");
-               audit_log_n_hex(ab, buf->data, buf->valid);
-               audit_log_end(ab);
-       }
+       tty_audit_log("tty", tsk, loginuid, sessionid, buf->major, buf->minor,
+                     buf->data, buf->valid);
        buf->valid = 0;
 }
 
@@ -149,6 +159,42 @@ void tty_audit_fork(struct signal_struct *sig)
        sig->tty_audit_buf = NULL;
 }
 
+/**
+ *     tty_audit_tiocsti       -       Log TIOCSTI
+ */
+void tty_audit_tiocsti(struct tty_struct *tty, char ch)
+{
+       struct tty_audit_buf *buf;
+       int major, minor, should_audit;
+
+       spin_lock_irq(&current->sighand->siglock);
+       should_audit = current->signal->audit_tty;
+       buf = current->signal->tty_audit_buf;
+       if (buf)
+               atomic_inc(&buf->count);
+       spin_unlock_irq(&current->sighand->siglock);
+
+       major = tty->driver->major;
+       minor = tty->driver->minor_start + tty->index;
+       if (buf) {
+               mutex_lock(&buf->mutex);
+               if (buf->major == major && buf->minor == minor)
+                       tty_audit_buf_push_current(buf);
+               mutex_unlock(&buf->mutex);
+               tty_audit_buf_put(buf);
+       }
+
+       if (should_audit && audit_enabled) {
+               uid_t auid;
+               unsigned int sessionid;
+
+               auid = audit_get_loginuid(current);
+               sessionid = audit_get_sessionid(current);
+               tty_audit_log("ioctl=TIOCSTI", current, auid, sessionid, major,
+                             minor, &ch, 1);
+       }
+}
+
 /**
  *     tty_audit_push_task     -       Flush task's pending audit data
  */
index 1412a8d..db15f9b 100644 (file)
@@ -2018,6 +2018,7 @@ static int tiocsti(struct tty_struct *tty, char __user *p)
                return -EPERM;
        if (get_user(ch, p))
                return -EFAULT;
+       tty_audit_tiocsti(tty, ch);
        ld = tty_ldisc_ref_wait(tty);
        ld->ops->receive_buf(tty, &ch, &mbz, 1);
        tty_ldisc_deref(ld);
index 5c9f67f..c5afc98 100644 (file)
@@ -106,6 +106,7 @@ void proc_id_connector(struct task_struct *task, int which_id)
        struct proc_event *ev;
        __u8 buffer[CN_PROC_MSG_SIZE];
        struct timespec ts;
+       const struct cred *cred;
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
@@ -115,14 +116,19 @@ void proc_id_connector(struct task_struct *task, int which_id)
        ev->what = which_id;
        ev->event_data.id.process_pid = task->pid;
        ev->event_data.id.process_tgid = task->tgid;
+       rcu_read_lock();
+       cred = __task_cred(task);
        if (which_id == PROC_EVENT_UID) {
-               ev->event_data.id.r.ruid = task->uid;
-               ev->event_data.id.e.euid = task->euid;
+               ev->event_data.id.r.ruid = cred->uid;
+               ev->event_data.id.e.euid = cred->euid;
        } else if (which_id == PROC_EVENT_GID) {
-               ev->event_data.id.r.rgid = task->gid;
-               ev->event_data.id.e.egid = task->egid;
-       } else
+               ev->event_data.id.r.rgid = cred->gid;
+               ev->event_data.id.e.egid = cred->egid;
+       } else {
+               rcu_read_unlock();
                return;
+       }
+       rcu_read_unlock();
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
index 4d22b21..0c79fe7 100644 (file)
@@ -38,9 +38,6 @@
 
 #include <asm/kmap_types.h>
 
-#undef dprintk
-
-#define HIFN_TEST
 //#define HIFN_DEBUG
 
 #ifdef HIFN_DEBUG
@@ -363,14 +360,14 @@ static atomic_t hifn_dev_number;
 #define HIFN_NAMESIZE                  32
 #define HIFN_MAX_RESULT_ORDER          5
 
-#define        HIFN_D_CMD_RSIZE                24*4
-#define        HIFN_D_SRC_RSIZE                80*4
-#define        HIFN_D_DST_RSIZE                80*4
-#define        HIFN_D_RES_RSIZE                24*4
+#define        HIFN_D_CMD_RSIZE                24*1
+#define        HIFN_D_SRC_RSIZE                80*1
+#define        HIFN_D_DST_RSIZE                80*1
+#define        HIFN_D_RES_RSIZE                24*1
 
 #define HIFN_D_DST_DALIGN              4
 
-#define HIFN_QUEUE_LENGTH              HIFN_D_CMD_RSIZE-1
+#define HIFN_QUEUE_LENGTH              (HIFN_D_CMD_RSIZE - 1)
 
 #define AES_MIN_KEY_SIZE               16
 #define AES_MAX_KEY_SIZE               32
@@ -406,8 +403,6 @@ struct hifn_dma {
        u8                      command_bufs[HIFN_D_CMD_RSIZE][HIFN_MAX_COMMAND];
        u8                      result_bufs[HIFN_D_CMD_RSIZE][HIFN_MAX_RESULT];
 
-       u64                     test_src, test_dst;
-
        /*
         *  Our current positions for insertion and removal from the descriptor
         *  rings.
@@ -434,9 +429,6 @@ struct hifn_device
        struct pci_dev          *pdev;
        void __iomem            *bar[3];
 
-       unsigned long           result_mem;
-       dma_addr_t              dst;
-
        void                    *desc_virt;
        dma_addr_t              desc_dma;
 
@@ -446,8 +438,6 @@ struct hifn_device
 
        spinlock_t              lock;
 
-       void                    *priv;
-
        u32                     flags;
        int                     active, started;
        struct delayed_work     work;
@@ -657,12 +647,17 @@ struct ablkcipher_walk
 
 struct hifn_context
 {
-       u8                      key[HIFN_MAX_CRYPT_KEY_LENGTH], *iv;
+       u8                      key[HIFN_MAX_CRYPT_KEY_LENGTH];
        struct hifn_device      *dev;
-       unsigned int            keysize, ivsize;
+       unsigned int            keysize;
+};
+
+struct hifn_request_context
+{
+       u8                      *iv;
+       unsigned int            ivsize;
        u8                      op, type, mode, unused;
        struct ablkcipher_walk  walk;
-       atomic_t                sg_num;
 };
 
 #define crypto_alg_to_hifn(a)  container_of(a, struct hifn_crypto_alg, alg)
@@ -1168,7 +1163,8 @@ static int hifn_setup_crypto_command(struct hifn_device *dev,
 }
 
 static int hifn_setup_cmd_desc(struct hifn_device *dev,
-               struct hifn_context *ctx, void *priv, unsigned int nbytes)
+               struct hifn_context *ctx, struct hifn_request_context *rctx,
+               void *priv, unsigned int nbytes)
 {
        struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
        int cmd_len, sa_idx;
@@ -1179,7 +1175,7 @@ static int hifn_setup_cmd_desc(struct hifn_device *dev,
        buf_pos = buf = dma->command_bufs[dma->cmdi];
 
        mask = 0;
-       switch (ctx->op) {
+       switch (rctx->op) {
                case ACRYPTO_OP_DECRYPT:
                        mask = HIFN_BASE_CMD_CRYPT | HIFN_BASE_CMD_DECODE;
                        break;
@@ -1196,15 +1192,15 @@ static int hifn_setup_cmd_desc(struct hifn_device *dev,
        buf_pos += hifn_setup_base_command(dev, buf_pos, nbytes,
                        nbytes, mask, dev->snum);
 
-       if (ctx->op == ACRYPTO_OP_ENCRYPT || ctx->op == ACRYPTO_OP_DECRYPT) {
+       if (rctx->op == ACRYPTO_OP_ENCRYPT || rctx->op == ACRYPTO_OP_DECRYPT) {
                u16 md = 0;
 
                if (ctx->keysize)
                        md |= HIFN_CRYPT_CMD_NEW_KEY;
-               if (ctx->iv && ctx->mode != ACRYPTO_MODE_ECB)
+               if (rctx->iv && rctx->mode != ACRYPTO_MODE_ECB)
                        md |= HIFN_CRYPT_CMD_NEW_IV;
 
-               switch (ctx->mode) {
+               switch (rctx->mode) {
                        case ACRYPTO_MODE_ECB:
                                md |= HIFN_CRYPT_CMD_MODE_ECB;
                                break;
@@ -1221,7 +1217,7 @@ static int hifn_setup_cmd_desc(struct hifn_device *dev,
                                goto err_out;
                }
 
-               switch (ctx->type) {
+               switch (rctx->type) {
                        case ACRYPTO_TYPE_AES_128:
                                if (ctx->keysize != 16)
                                        goto err_out;
@@ -1256,17 +1252,18 @@ static int hifn_setup_cmd_desc(struct hifn_device *dev,
 
                buf_pos += hifn_setup_crypto_command(dev, buf_pos,
                                nbytes, nbytes, ctx->key, ctx->keysize,
-                               ctx->iv, ctx->ivsize, md);
+                               rctx->iv, rctx->ivsize, md);
        }
 
        dev->sa[sa_idx] = priv;
+       dev->started++;
 
        cmd_len = buf_pos - buf;
        dma->cmdr[dma->cmdi].l = __cpu_to_le32(cmd_len | HIFN_D_VALID |
                        HIFN_D_LAST | HIFN_D_MASKDONEIRQ);
 
        if (++dma->cmdi == HIFN_D_CMD_RSIZE) {
-               dma->cmdr[dma->cmdi].l = __cpu_to_le32(HIFN_MAX_COMMAND |
+               dma->cmdr[dma->cmdi].l = __cpu_to_le32(
                        HIFN_D_VALID | HIFN_D_LAST |
                        HIFN_D_MASKDONEIRQ | HIFN_D_JUMP);
                dma->cmdi = 0;
@@ -1284,7 +1281,7 @@ err_out:
 }
 
 static int hifn_setup_src_desc(struct hifn_device *dev, struct page *page,
-               unsigned int offset, unsigned int size)
+               unsigned int offset, unsigned int size, int last)
 {
        struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
        int idx;
@@ -1296,12 +1293,12 @@ static int hifn_setup_src_desc(struct hifn_device *dev, struct page *page,
 
        dma->srcr[idx].p = __cpu_to_le32(addr);
        dma->srcr[idx].l = __cpu_to_le32(size | HIFN_D_VALID |
-                       HIFN_D_MASKDONEIRQ | HIFN_D_LAST);
+                       HIFN_D_MASKDONEIRQ | (last ? HIFN_D_LAST : 0));
 
        if (++idx == HIFN_D_SRC_RSIZE) {
                dma->srcr[idx].l = __cpu_to_le32(HIFN_D_VALID |
-                               HIFN_D_JUMP |
-                               HIFN_D_MASKDONEIRQ | HIFN_D_LAST);
+                               HIFN_D_JUMP | HIFN_D_MASKDONEIRQ |
+                               (last ? HIFN_D_LAST : 0));
                idx = 0;
        }
 
@@ -1342,7 +1339,7 @@ static void hifn_setup_res_desc(struct hifn_device *dev)
 }
 
 static void hifn_setup_dst_desc(struct hifn_device *dev, struct page *page,
-               unsigned offset, unsigned size)
+               unsigned offset, unsigned size, int last)
 {
        struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
        int idx;
@@ -1353,12 +1350,12 @@ static void hifn_setup_dst_desc(struct hifn_device *dev, struct page *page,
        idx = dma->dsti;
        dma->dstr[idx].p = __cpu_to_le32(addr);
        dma->dstr[idx].l = __cpu_to_le32(size | HIFN_D_VALID |
-                       HIFN_D_MASKDONEIRQ | HIFN_D_LAST);
+                       HIFN_D_MASKDONEIRQ | (last ? HIFN_D_LAST : 0));
 
        if (++idx == HIFN_D_DST_RSIZE) {
                dma->dstr[idx].l = __cpu_to_le32(HIFN_D_VALID |
                                HIFN_D_JUMP | HIFN_D_MASKDONEIRQ |
-                               HIFN_D_LAST);
+                               (last ? HIFN_D_LAST : 0));
                idx = 0;
        }
        dma->dsti = idx;
@@ -1370,16 +1367,52 @@ static void hifn_setup_dst_desc(struct hifn_device *dev, struct page *page,
        }
 }
 
-static int hifn_setup_dma(struct hifn_device *dev, struct page *spage, unsigned int soff,
-               struct page *dpage, unsigned int doff, unsigned int nbytes, void *priv,
-               struct hifn_context *ctx)
+static int hifn_setup_dma(struct hifn_device *dev,
+               struct hifn_context *ctx, struct hifn_request_context *rctx,
+               struct scatterlist *src, struct scatterlist *dst,
+               unsigned int nbytes, void *priv)
 {
-       dprintk("%s: spage: %p, soffset: %u, dpage: %p, doffset: %u, nbytes: %u, priv: %p, ctx: %p.\n",
-                       dev->name, spage, soff, dpage, doff, nbytes, priv, ctx);
+       struct scatterlist *t;
+       struct page *spage, *dpage;
+       unsigned int soff, doff;
+       unsigned int n, len;
 
-       hifn_setup_src_desc(dev, spage, soff, nbytes);
-       hifn_setup_cmd_desc(dev, ctx, priv, nbytes);
-       hifn_setup_dst_desc(dev, dpage, doff, nbytes);
+       n = nbytes;
+       while (n) {
+               spage = sg_page(src);
+               soff = src->offset;
+               len = min(src->length, n);
+
+               hifn_setup_src_desc(dev, spage, soff, len, n - len == 0);
+
+               src++;
+               n -= len;
+       }
+
+       t = &rctx->walk.cache[0];
+       n = nbytes;
+       while (n) {
+               if (t->length && rctx->walk.flags & ASYNC_FLAGS_MISALIGNED) {
+                       BUG_ON(!sg_page(t));
+                       dpage = sg_page(t);
+                       doff = 0;
+                       len = t->length;
+               } else {
+                       BUG_ON(!sg_page(dst));
+                       dpage = sg_page(dst);
+                       doff = dst->offset;
+                       len = dst->length;
+               }
+               len = min(len, n);
+
+               hifn_setup_dst_desc(dev, dpage, doff, len, n - len == 0);
+
+               dst++;
+               t++;
+               n -= len;
+       }
+
+       hifn_setup_cmd_desc(dev, ctx, rctx, priv, nbytes);
        hifn_setup_res_desc(dev);
        return 0;
 }
@@ -1424,32 +1457,26 @@ static void ablkcipher_walk_exit(struct ablkcipher_walk *w)
        w->num = 0;
 }
 
-static int ablkcipher_add(void *daddr, unsigned int *drestp, struct scatterlist *src,
+static int ablkcipher_add(unsigned int *drestp, struct scatterlist *dst,
                unsigned int size, unsigned int *nbytesp)
 {
        unsigned int copy, drest = *drestp, nbytes = *nbytesp;
        int idx = 0;
-       void *saddr;
 
        if (drest < size || size > nbytes)
                return -EINVAL;
 
        while (size) {
-               copy = min(drest, min(size, src->length));
-
-               saddr = kmap_atomic(sg_page(src), KM_SOFTIRQ1);
-               memcpy(daddr, saddr + src->offset, copy);
-               kunmap_atomic(saddr, KM_SOFTIRQ1);
+               copy = min(drest, min(size, dst->length));
 
                size -= copy;
                drest -= copy;
                nbytes -= copy;
-               daddr += copy;
 
                dprintk("%s: copy: %u, size: %u, drest: %u, nbytes: %u.\n",
                                __func__, copy, size, drest, nbytes);
 
-               src++;
+               dst++;
                idx++;
        }
 
@@ -1462,8 +1489,7 @@ static int ablkcipher_add(void *daddr, unsigned int *drestp, struct scatterlist
 static int ablkcipher_walk(struct ablkcipher_request *req,
                struct ablkcipher_walk *w)
 {
-       struct scatterlist *src, *dst, *t;
-       void *daddr;
+       struct scatterlist *dst, *t;
        unsigned int nbytes = req->nbytes, offset, copy, diff;
        int idx, tidx, err;
 
@@ -1473,26 +1499,22 @@ static int ablkcipher_walk(struct ablkcipher_request *req,
                if (idx >= w->num && (w->flags & ASYNC_FLAGS_MISALIGNED))
                        return -EINVAL;
 
-               src = &req->src[idx];
                dst = &req->dst[idx];
 
-               dprintk("\n%s: slen: %u, dlen: %u, soff: %u, doff: %u, offset: %u, "
-                               "nbytes: %u.\n",
-                               __func__, src->length, dst->length, src->offset,
-                               dst->offset, offset, nbytes);
+               dprintk("\n%s: dlen: %u, doff: %u, offset: %u, nbytes: %u.\n",
+                       __func__, dst->length, dst->offset, offset, nbytes);
 
                if (!IS_ALIGNED(dst->offset, HIFN_D_DST_DALIGN) ||
                    !IS_ALIGNED(dst->length, HIFN_D_DST_DALIGN) ||
                    offset) {
-                       unsigned slen = min(src->length - offset, nbytes);
+                       unsigned slen = min(dst->length - offset, nbytes);
                        unsigned dlen = PAGE_SIZE;
 
                        t = &w->cache[idx];
 
-                       daddr = kmap_atomic(sg_page(t), KM_SOFTIRQ0);
-                       err = ablkcipher_add(daddr, &dlen, src, slen, &nbytes);
+                       err = ablkcipher_add(&dlen, dst, slen, &nbytes);
                        if (err < 0)
-                               goto err_out_unmap;
+                               return err;
 
                        idx += err;
 
@@ -1528,21 +1550,19 @@ static int ablkcipher_walk(struct ablkcipher_request *req,
                        } else {
                                copy += diff + nbytes;
 
-                               src = &req->src[idx];
+                               dst = &req->dst[idx];
 
-                               err = ablkcipher_add(daddr + slen, &dlen, src, nbytes, &nbytes);
+                               err = ablkcipher_add(&dlen, dst, nbytes, &nbytes);
                                if (err < 0)
-                                       goto err_out_unmap;
+                                       return err;
 
                                idx += err;
                        }
 
                        t->length = copy;
                        t->offset = offset;
-
-                       kunmap_atomic(daddr, KM_SOFTIRQ0);
                } else {
-                       nbytes -= min(src->length, nbytes);
+                       nbytes -= min(dst->length, nbytes);
                        idx++;
                }
 
@@ -1550,26 +1570,22 @@ static int ablkcipher_walk(struct ablkcipher_request *req,
        }
 
        return tidx;
-
-err_out_unmap:
-       kunmap_atomic(daddr, KM_SOFTIRQ0);
-       return err;
 }
 
 static int hifn_setup_session(struct ablkcipher_request *req)
 {
        struct hifn_context *ctx = crypto_tfm_ctx(req->base.tfm);
+       struct hifn_request_context *rctx = ablkcipher_request_ctx(req);
        struct hifn_device *dev = ctx->dev;
-       struct page *spage, *dpage;
-       unsigned long soff, doff, dlen, flags;
-       unsigned int nbytes = req->nbytes, idx = 0, len;
+       unsigned long dlen, flags;
+       unsigned int nbytes = req->nbytes, idx = 0;
        int err = -EINVAL, sg_num;
-       struct scatterlist *src, *dst, *t;
+       struct scatterlist *dst;
 
-       if (ctx->iv && !ctx->ivsize && ctx->mode != ACRYPTO_MODE_ECB)
+       if (rctx->iv && !rctx->ivsize && rctx->mode != ACRYPTO_MODE_ECB)
                goto err_out_exit;
 
-       ctx->walk.flags = 0;
+       rctx->walk.flags = 0;
 
        while (nbytes) {
                dst = &req->dst[idx];
@@ -1577,27 +1593,23 @@ static int hifn_setup_session(struct ablkcipher_request *req)
 
                if (!IS_ALIGNED(dst->offset, HIFN_D_DST_DALIGN) ||
                    !IS_ALIGNED(dlen, HIFN_D_DST_DALIGN))
-                       ctx->walk.flags |= ASYNC_FLAGS_MISALIGNED;
+                       rctx->walk.flags |= ASYNC_FLAGS_MISALIGNED;
 
                nbytes -= dlen;
                idx++;
        }
 
-       if (ctx->walk.flags & ASYNC_FLAGS_MISALIGNED) {
-               err = ablkcipher_walk_init(&ctx->walk, idx, GFP_ATOMIC);
+       if (rctx->walk.flags & ASYNC_FLAGS_MISALIGNED) {
+               err = ablkcipher_walk_init(&rctx->walk, idx, GFP_ATOMIC);
                if (err < 0)
                        return err;
        }
 
-       nbytes = req->nbytes;
-       idx = 0;
-
-       sg_num = ablkcipher_walk(req, &ctx->walk);
+       sg_num = ablkcipher_walk(req, &rctx->walk);
        if (sg_num < 0) {
                err = sg_num;
                goto err_out_exit;
        }
-       atomic_set(&ctx->sg_num, sg_num);
 
        spin_lock_irqsave(&dev->lock, flags);
        if (dev->started + sg_num > HIFN_QUEUE_LENGTH) {
@@ -1605,37 +1617,11 @@ static int hifn_setup_session(struct ablkcipher_request *req)
                goto err_out;
        }
 
-       dev->snum++;
-       dev->started += sg_num;
-
-       while (nbytes) {
-               src = &req->src[idx];
-               dst = &req->dst[idx];
-               t = &ctx->walk.cache[idx];
-
-               if (t->length) {
-                       spage = dpage = sg_page(t);
-                       soff = doff = 0;
-                       len = t->length;
-               } else {
-                       spage = sg_page(src);
-                       soff = src->offset;
-
-                       dpage = sg_page(dst);
-                       doff = dst->offset;
-
-                       len = dst->length;
-               }
-
-               idx++;
-
-               err = hifn_setup_dma(dev, spage, soff, dpage, doff, nbytes,
-                               req, ctx);
-               if (err)
-                       goto err_out;
+       err = hifn_setup_dma(dev, ctx, rctx, req->src, req->dst, req->nbytes, req);
+       if (err)
+               goto err_out;
 
-               nbytes -= min(len, nbytes);
-       }
+       dev->snum++;
 
        dev->active = HIFN_DEFAULT_ACTIVE_NUM;
        spin_unlock_irqrestore(&dev->lock, flags);
@@ -1645,12 +1631,13 @@ static int hifn_setup_session(struct ablkcipher_request *req)
 err_out:
        spin_unlock_irqrestore(&dev->lock, flags);
 err_out_exit:
-       if (err)
-               dprintk("%s: iv: %p [%d], key: %p [%d], mode: %u, op: %u, "
+       if (err) {
+               printk("%s: iv: %p [%d], key: %p [%d], mode: %u, op: %u, "
                                "type: %u, err: %d.\n",
-                       dev->name, ctx->iv, ctx->ivsize,
+                       dev->name, rctx->iv, rctx->ivsize,
                        ctx->key, ctx->keysize,
-                       ctx->mode, ctx->op, ctx->type, err);
+                       rctx->mode, rctx->op, rctx->type, err);
+       }
 
        return err;
 }
@@ -1660,31 +1647,33 @@ static int hifn_test(struct hifn_device *dev, int encdec, u8 snum)
        int n, err;
        u8 src[16];
        struct hifn_context ctx;
+       struct hifn_request_context rctx;
        u8 fips_aes_ecb_from_zero[16] = {
                0x66, 0xE9, 0x4B, 0xD4,
                0xEF, 0x8A, 0x2C, 0x3B,
                0x88, 0x4C, 0xFA, 0x59,
                0xCA, 0x34, 0x2B, 0x2E};
+       struct scatterlist sg;
 
        memset(src, 0, sizeof(src));
        memset(ctx.key, 0, sizeof(ctx.key));
 
        ctx.dev = dev;
        ctx.keysize = 16;
-       ctx.ivsize = 0;
-       ctx.iv = NULL;
-       ctx.op = (encdec)?ACRYPTO_OP_ENCRYPT:ACRYPTO_OP_DECRYPT;
-       ctx.mode = ACRYPTO_MODE_ECB;
-       ctx.type = ACRYPTO_TYPE_AES_128;
-       atomic_set(&ctx.sg_num, 1);
-
-       err = hifn_setup_dma(dev,
-                       virt_to_page(src), offset_in_page(src),
-                       virt_to_page(src), offset_in_page(src),
-                       sizeof(src), NULL, &ctx);
+       rctx.ivsize = 0;
+       rctx.iv = NULL;
+       rctx.op = (encdec)?ACRYPTO_OP_ENCRYPT:ACRYPTO_OP_DECRYPT;
+       rctx.mode = ACRYPTO_MODE_ECB;
+       rctx.type = ACRYPTO_TYPE_AES_128;
+       rctx.walk.cache[0].length = 0;
+
+       sg_init_one(&sg, &src, sizeof(src));
+
+       err = hifn_setup_dma(dev, &ctx, &rctx, &sg, &sg, sizeof(src), NULL);
        if (err)
                goto err_out;
 
+       dev->started = 0;
        msleep(200);
 
        dprintk("%s: decoded: ", dev->name);
@@ -1711,6 +1700,7 @@ static int hifn_start_device(struct hifn_device *dev)
 {
        int err;
 
+       dev->started = dev->active = 0;
        hifn_reset_dma(dev, 1);
 
        err = hifn_enable_crypto(dev);
@@ -1764,90 +1754,65 @@ static int ablkcipher_get(void *saddr, unsigned int *srestp, unsigned int offset
        return idx;
 }
 
-static void hifn_process_ready(struct ablkcipher_request *req, int error)
+static inline void hifn_complete_sa(struct hifn_device *dev, int i)
 {
-       struct hifn_context *ctx = crypto_tfm_ctx(req->base.tfm);
-       struct hifn_device *dev;
-
-       dprintk("%s: req: %p, ctx: %p.\n", __func__, req, ctx);
+       unsigned long flags;
 
-       dev = ctx->dev;
-       dprintk("%s: req: %p, started: %d, sg_num: %d.\n",
-               __func__, req, dev->started, atomic_read(&ctx->sg_num));
+       spin_lock_irqsave(&dev->lock, flags);
+       dev->sa[i] = NULL;
+       dev->started--;
+       if (dev->started < 0)
+               printk("%s: started: %d.\n", __func__, dev->started);
+       spin_unlock_irqrestore(&dev->lock, flags);
+       BUG_ON(dev->started < 0);
+}
 
-       if (--dev->started < 0)
-               BUG();
+static void hifn_process_ready(struct ablkcipher_request *req, int error)
+{
+       struct hifn_request_context *rctx = ablkcipher_request_ctx(req);
 
-       if (atomic_dec_and_test(&ctx->sg_num)) {
+       if (rctx->walk.flags & ASYNC_FLAGS_MISALIGNED) {
                unsigned int nbytes = req->nbytes;
                int idx = 0, err;
                struct scatterlist *dst, *t;
                void *saddr;
 
-               if (ctx->walk.flags & ASYNC_FLAGS_MISALIGNED) {
-                       while (nbytes) {
-                               t = &ctx->walk.cache[idx];
-                               dst = &req->dst[idx];
-
-                               dprintk("\n%s: sg_page(t): %p, t->length: %u, "
-                                       "sg_page(dst): %p, dst->length: %u, "
-                                       "nbytes: %u.\n",
-                                       __func__, sg_page(t), t->length,
-                                       sg_page(dst), dst->length, nbytes);
+               while (nbytes) {
+                       t = &rctx->walk.cache[idx];
+                       dst = &req->dst[idx];
 
-                               if (!t->length) {
-                                       nbytes -= min(dst->length, nbytes);
-                                       idx++;
-                                       continue;
-                               }
+                       dprintk("\n%s: sg_page(t): %p, t->length: %u, "
+                               "sg_page(dst): %p, dst->length: %u, "
+                               "nbytes: %u.\n",
+                               __func__, sg_page(t), t->length,
+                               sg_page(dst), dst->length, nbytes);
 
-                               saddr = kmap_atomic(sg_page(t), KM_IRQ1);
+                       if (!t->length) {
+                               nbytes -= min(dst->length, nbytes);
+                               idx++;
+                               continue;
+                       }
 
-                               err = ablkcipher_get(saddr, &t->length, t->offset,
-                                               dst, nbytes, &nbytes);
-                               if (err < 0) {
-                                       kunmap_atomic(saddr, KM_IRQ1);
-                                       break;
-                               }
+                       saddr = kmap_atomic(sg_page(t), KM_SOFTIRQ0);
 
-                               idx += err;
-                               kunmap_atomic(saddr, KM_IRQ1);
+                       err = ablkcipher_get(saddr, &t->length, t->offset,
+                                       dst, nbytes, &nbytes);
+                       if (err < 0) {
+                               kunmap_atomic(saddr, KM_SOFTIRQ0);
+                               break;
                        }
 
-                       ablkcipher_walk_exit(&ctx->walk);
+                       idx += err;
+                       kunmap_atomic(saddr, KM_SOFTIRQ0);
                }
 
-               req->base.complete(&req->base, error);
+               ablkcipher_walk_exit(&rctx->walk);
        }
-}
 
-static void hifn_check_for_completion(struct hifn_device *dev, int error)
-{
-       int i;
-       struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
-
-       for (i=0; i<HIFN_D_RES_RSIZE; ++i) {
-               struct hifn_desc *d = &dma->resr[i];
-
-               if (!(d->l & __cpu_to_le32(HIFN_D_VALID)) && dev->sa[i]) {
-                       dev->success++;
-                       dev->reset = 0;
-                       hifn_process_ready(dev->sa[i], error);
-                       dev->sa[i] = NULL;
-               }
-
-               if (d->l & __cpu_to_le32(HIFN_D_DESTOVER | HIFN_D_OVER))
-                       if (printk_ratelimit())
-                               printk("%s: overflow detected [d: %u, o: %u] "
-                                               "at %d resr: l: %08x, p: %08x.\n",
-                                       dev->name,
-                                       !!(d->l & __cpu_to_le32(HIFN_D_DESTOVER)),
-                                       !!(d->l & __cpu_to_le32(HIFN_D_OVER)),
-                                       i, d->l, d->p);
-       }
+       req->base.complete(&req->base, error);
 }
 
-static void hifn_clear_rings(struct hifn_device *dev)
+static void hifn_clear_rings(struct hifn_device *dev, int error)
 {
        struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
        int i, u;
@@ -1864,21 +1829,26 @@ static void hifn_clear_rings(struct hifn_device *dev)
                if (dma->resr[i].l & __cpu_to_le32(HIFN_D_VALID))
                        break;
 
-               if (i != HIFN_D_RES_RSIZE)
-                       u--;
+               if (dev->sa[i]) {
+                       dev->success++;
+                       dev->reset = 0;
+                       hifn_process_ready(dev->sa[i], error);
+                       hifn_complete_sa(dev, i);
+               }
 
-               if (++i == (HIFN_D_RES_RSIZE + 1))
+               if (++i == HIFN_D_RES_RSIZE)
                        i = 0;
+               u--;
        }
        dma->resk = i; dma->resu = u;
 
        i = dma->srck; u = dma->srcu;
        while (u != 0) {
-               if (i == HIFN_D_SRC_RSIZE)
-                       i = 0;
                if (dma->srcr[i].l & __cpu_to_le32(HIFN_D_VALID))
                        break;
-               i++, u--;
+               if (++i == HIFN_D_SRC_RSIZE)
+                       i = 0;
+               u--;
        }
        dma->srck = i; dma->srcu = u;
 
@@ -1886,20 +1856,19 @@ static void hifn_clear_rings(struct hifn_device *dev)
        while (u != 0) {
                if (dma->cmdr[i].l & __cpu_to_le32(HIFN_D_VALID))
                        break;
-               if (i != HIFN_D_CMD_RSIZE)
-                       u--;
-               if (++i == (HIFN_D_CMD_RSIZE + 1))
+               if (++i == HIFN_D_CMD_RSIZE)
                        i = 0;
+               u--;
        }
        dma->cmdk = i; dma->cmdu = u;
 
        i = dma->dstk; u = dma->dstu;
        while (u != 0) {
-               if (i == HIFN_D_DST_RSIZE)
-                       i = 0;
                if (dma->dstr[i].l & __cpu_to_le32(HIFN_D_VALID))
                        break;
-               i++, u--;
+               if (++i == HIFN_D_DST_RSIZE)
+                       i = 0;
+               u--;
        }
        dma->dstk = i; dma->dstu = u;
 
@@ -1944,30 +1913,39 @@ static void hifn_work(struct work_struct *work)
        } else
                dev->active--;
 
-       if (dev->prev_success == dev->success && dev->started)
+       if ((dev->prev_success == dev->success) && dev->started)
                reset = 1;
        dev->prev_success = dev->success;
        spin_unlock_irqrestore(&dev->lock, flags);
 
        if (reset) {
-               dprintk("%s: r: %08x, active: %d, started: %d, "
-                               "success: %lu: reset: %d.\n",
-                       dev->name, r, dev->active, dev->started,
-                       dev->success, reset);
-
                if (++dev->reset >= 5) {
-                       dprintk("%s: really hard reset.\n", dev->name);
+                       int i;
+                       struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
+
+                       printk("%s: r: %08x, active: %d, started: %d, "
+                               "success: %lu: qlen: %u/%u, reset: %d.\n",
+                               dev->name, r, dev->active, dev->started,
+                               dev->success, dev->queue.qlen, dev->queue.max_qlen,
+                               reset);
+
+                       printk("%s: res: ", __func__);
+                       for (i=0; i<HIFN_D_RES_RSIZE; ++i) {
+                               printk("%x.%p ", dma->resr[i].l, dev->sa[i]);
+                               if (dev->sa[i]) {
+                                       hifn_process_ready(dev->sa[i], -ENODEV);
+                                       hifn_complete_sa(dev, i);
+                               }
+                       }
+                       printk("\n");
+
                        hifn_reset_dma(dev, 1);
                        hifn_stop_device(dev);
                        hifn_start_device(dev);
                        dev->reset = 0;
                }
 
-               spin_lock_irqsave(&dev->lock, flags);
-               hifn_check_for_completion(dev, -EBUSY);
-               hifn_clear_rings(dev);
-               dev->started = 0;
-               spin_unlock_irqrestore(&dev->lock, flags);
+               tasklet_schedule(&dev->tasklet);
        }
 
        schedule_delayed_work(&dev->work, HZ);
@@ -1984,8 +1962,8 @@ static irqreturn_t hifn_interrupt(int irq, void *data)
        dprintk("%s: 1 dmacsr: %08x, dmareg: %08x, res: %08x [%d], "
                        "i: %d.%d.%d.%d, u: %d.%d.%d.%d.\n",
                dev->name, dmacsr, dev->dmareg, dmacsr & dev->dmareg, dma->cmdi,
-               dma->cmdu, dma->srcu, dma->dstu, dma->resu,
-               dma->cmdi, dma->srci, dma->dsti, dma->resi);
+               dma->cmdi, dma->srci, dma->dsti, dma->resi,
+               dma->cmdu, dma->srcu, dma->dstu, dma->resu);
 
        if ((dmacsr & dev->dmareg) == 0)
                return IRQ_NONE;
@@ -2002,11 +1980,10 @@ static irqreturn_t hifn_interrupt(int irq, void *data)
        if (restart) {
                u32 puisr = hifn_read_0(dev, HIFN_0_PUISR);
 
-               if (printk_ratelimit())
-                       printk("%s: overflow: r: %d, d: %d, puisr: %08x, d: %u.\n",
-                               dev->name, !!(dmacsr & HIFN_DMACSR_R_OVER),
-                               !!(dmacsr & HIFN_DMACSR_D_OVER),
-                               puisr, !!(puisr & HIFN_PUISR_DSTOVER));
+               printk(KERN_WARNING "%s: overflow: r: %d, d: %d, puisr: %08x, d: %u.\n",
+                       dev->name, !!(dmacsr & HIFN_DMACSR_R_OVER),
+                       !!(dmacsr & HIFN_DMACSR_D_OVER),
+                       puisr, !!(puisr & HIFN_PUISR_DSTOVER));
                if (!!(puisr & HIFN_PUISR_DSTOVER))
                        hifn_write_0(dev, HIFN_0_PUISR, HIFN_PUISR_DSTOVER);
                hifn_write_1(dev, HIFN_1_DMA_CSR, dmacsr & (HIFN_DMACSR_R_OVER |
@@ -2016,12 +1993,11 @@ static irqreturn_t hifn_interrupt(int irq, void *data)
        restart = dmacsr & (HIFN_DMACSR_C_ABORT | HIFN_DMACSR_S_ABORT |
                        HIFN_DMACSR_D_ABORT | HIFN_DMACSR_R_ABORT);
        if (restart) {
-               if (printk_ratelimit())
-                       printk("%s: abort: c: %d, s: %d, d: %d, r: %d.\n",
-                               dev->name, !!(dmacsr & HIFN_DMACSR_C_ABORT),
-                               !!(dmacsr & HIFN_DMACSR_S_ABORT),
-                               !!(dmacsr & HIFN_DMACSR_D_ABORT),
-                               !!(dmacsr & HIFN_DMACSR_R_ABORT));
+               printk(KERN_WARNING "%s: abort: c: %d, s: %d, d: %d, r: %d.\n",
+                       dev->name, !!(dmacsr & HIFN_DMACSR_C_ABORT),
+                       !!(dmacsr & HIFN_DMACSR_S_ABORT),
+                       !!(dmacsr & HIFN_DMACSR_D_ABORT),
+                       !!(dmacsr & HIFN_DMACSR_R_ABORT));
                hifn_reset_dma(dev, 1);
                hifn_init_dma(dev);
                hifn_init_registers(dev);
@@ -2034,7 +2010,6 @@ static irqreturn_t hifn_interrupt(int irq, void *data)
        }
 
        tasklet_schedule(&dev->tasklet);
-       hifn_clear_rings(dev);
 
        return IRQ_HANDLED;
 }
@@ -2048,21 +2023,25 @@ static void hifn_flush(struct hifn_device *dev)
        struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
        int i;
 
-       spin_lock_irqsave(&dev->lock, flags);
        for (i=0; i<HIFN_D_RES_RSIZE; ++i) {
                struct hifn_desc *d = &dma->resr[i];
 
                if (dev->sa[i]) {
                        hifn_process_ready(dev->sa[i],
                                (d->l & __cpu_to_le32(HIFN_D_VALID))?-ENODEV:0);
+                       hifn_complete_sa(dev, i);
                }
        }
 
+       spin_lock_irqsave(&dev->lock, flags);
        while ((async_req = crypto_dequeue_request(&dev->queue))) {
                ctx = crypto_tfm_ctx(async_req->tfm);
                req = container_of(async_req, struct ablkcipher_request, base);
+               spin_unlock_irqrestore(&dev->lock, flags);
 
                hifn_process_ready(req, -ENODEV);
+
+               spin_lock_irqsave(&dev->lock, flags);
        }
        spin_unlock_irqrestore(&dev->lock, flags);
 }
@@ -2121,6 +2100,7 @@ static int hifn_setup_crypto_req(struct ablkcipher_request *req, u8 op,
                u8 type, u8 mode)
 {
        struct hifn_context *ctx = crypto_tfm_ctx(req->base.tfm);
+       struct hifn_request_context *rctx = ablkcipher_request_ctx(req);
        unsigned ivsize;
 
        ivsize = crypto_ablkcipher_ivsize(crypto_ablkcipher_reqtfm(req));
@@ -2141,11 +2121,11 @@ static int hifn_setup_crypto_req(struct ablkcipher_request *req, u8 op,
                        type = ACRYPTO_TYPE_AES_256;
        }
 
-       ctx->op = op;
-       ctx->mode = mode;
-       ctx->type = type;
-       ctx->iv = req->info;
-       ctx->ivsize = ivsize;
+       rctx->op = op;
+       rctx->mode = mode;
+       rctx->type = type;
+       rctx->iv = req->info;
+       rctx->ivsize = ivsize;
 
        /*
         * HEAVY TODO: needs to kick Herbert XU to write documentation.
@@ -2158,7 +2138,7 @@ static int hifn_setup_crypto_req(struct ablkcipher_request *req, u8 op,
 
 static int hifn_process_queue(struct hifn_device *dev)
 {
-       struct crypto_async_request *async_req;
+       struct crypto_async_request *async_req, *backlog;
        struct hifn_context *ctx;
        struct ablkcipher_request *req;
        unsigned long flags;
@@ -2166,12 +2146,16 @@ static int hifn_process_queue(struct hifn_device *dev)
 
        while (dev->started < HIFN_QUEUE_LENGTH) {
                spin_lock_irqsave(&dev->lock, flags);
+               backlog = crypto_get_backlog(&dev->queue);
                async_req = crypto_dequeue_request(&dev->queue);
                spin_unlock_irqrestore(&dev->lock, flags);
 
                if (!async_req)
                        break;
 
+               if (backlog)
+                       backlog->complete(backlog, -EINPROGRESS);
+
                ctx = crypto_tfm_ctx(async_req->tfm);
                req = container_of(async_req, struct ablkcipher_request, base);
 
@@ -2496,7 +2480,7 @@ static int hifn_cra_init(struct crypto_tfm *tfm)
        struct hifn_context *ctx = crypto_tfm_ctx(tfm);
 
        ctx->dev = ha->dev;
-
+       tfm->crt_ablkcipher.reqsize = sizeof(struct hifn_request_context);
        return 0;
 }
 
@@ -2574,7 +2558,10 @@ static void hifn_tasklet_callback(unsigned long data)
         * (like dev->success), but they are used in process
         * context or update is atomic (like setting dev->sa[i] to NULL).
         */
-       hifn_check_for_completion(dev, 0);
+       hifn_clear_rings(dev, 0);
+
+       if (dev->started < HIFN_QUEUE_LENGTH && dev->queue.qlen)
+               hifn_process_queue(dev);
 }
 
 static int hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)
@@ -2631,22 +2618,11 @@ static int hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                        goto err_out_unmap_bars;
        }
 
-       dev->result_mem = __get_free_pages(GFP_KERNEL, HIFN_MAX_RESULT_ORDER);
-       if (!dev->result_mem) {
-               dprintk("Failed to allocate %d pages for result_mem.\n",
-                               HIFN_MAX_RESULT_ORDER);
-               goto err_out_unmap_bars;
-       }
-       memset((void *)dev->result_mem, 0, PAGE_SIZE*(1<<HIFN_MAX_RESULT_ORDER));
-
-       dev->dst = pci_map_single(pdev, (void *)dev->result_mem,
-                       PAGE_SIZE << HIFN_MAX_RESULT_ORDER, PCI_DMA_FROMDEVICE);
-
        dev->desc_virt = pci_alloc_consistent(pdev, sizeof(struct hifn_dma),
                        &dev->desc_dma);
        if (!dev->desc_virt) {
                dprintk("Failed to allocate descriptor rings.\n");
-               goto err_out_free_result_pages;
+               goto err_out_unmap_bars;
        }
        memset(dev->desc_virt, 0, sizeof(struct hifn_dma));
 
@@ -2706,11 +2682,6 @@ err_out_free_desc:
        pci_free_consistent(pdev, sizeof(struct hifn_dma),
                        dev->desc_virt, dev->desc_dma);
 
-err_out_free_result_pages:
-       pci_unmap_single(pdev, dev->dst, PAGE_SIZE << HIFN_MAX_RESULT_ORDER,
-                       PCI_DMA_FROMDEVICE);
-       free_pages(dev->result_mem, HIFN_MAX_RESULT_ORDER);
-
 err_out_unmap_bars:
        for (i=0; i<3; ++i)
                if (dev->bar[i])
@@ -2748,10 +2719,6 @@ static void hifn_remove(struct pci_dev *pdev)
 
                pci_free_consistent(pdev, sizeof(struct hifn_dma),
                                dev->desc_virt, dev->desc_dma);
-               pci_unmap_single(pdev, dev->dst,
-                               PAGE_SIZE << HIFN_MAX_RESULT_ORDER,
-                               PCI_DMA_FROMDEVICE);
-               free_pages(dev->result_mem, HIFN_MAX_RESULT_ORDER);
                for (i=0; i<3; ++i)
                        if (dev->bar[i])
                                iounmap(dev->bar[i]);
@@ -2782,6 +2749,11 @@ static int __devinit hifn_init(void)
        unsigned int freq;
        int err;
 
+       if (sizeof(dma_addr_t) > 4) {
+               printk(KERN_INFO "HIFN supports only 32-bit addresses.\n");
+               return -EINVAL;
+       }
+
        if (strncmp(hifn_pll_ref, "ext", 3) &&
            strncmp(hifn_pll_ref, "pci", 3)) {
                printk(KERN_ERR "hifn795x: invalid hifn_pll_ref clock, "
index bf2917d..856b3cc 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/errno.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
+#include <linux/percpu.h>
+#include <linux/smp.h>
 #include <asm/byteorder.h>
 #include <asm/i387.h>
 #include "padlock.h"
@@ -49,6 +51,8 @@ struct aes_ctx {
        u32 *D;
 };
 
+static DEFINE_PER_CPU(struct cword *, last_cword);
+
 /* Tells whether the ACE is capable to generate
    the extended key for a given key_len. */
 static inline int
@@ -89,6 +93,7 @@ static int aes_set_key(struct crypto_tfm *tfm, const u8 *in_key,
        const __le32 *key = (const __le32 *)in_key;
        u32 *flags = &tfm->crt_flags;
        struct crypto_aes_ctx gen_aes;
+       int cpu;
 
        if (key_len % 8) {
                *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
@@ -118,7 +123,7 @@ static int aes_set_key(struct crypto_tfm *tfm, const u8 *in_key,
 
        /* Don't generate extended keys if the hardware can do it. */
        if (aes_hw_extkey_available(key_len))
-               return 0;
+               goto ok;
 
        ctx->D = ctx->d_data;
        ctx->cword.encrypt.keygen = 1;
@@ -131,15 +136,30 @@ static int aes_set_key(struct crypto_tfm *tfm, const u8 *in_key,
 
        memcpy(ctx->E, gen_aes.key_enc, AES_MAX_KEYLENGTH);
        memcpy(ctx->D, gen_aes.key_dec, AES_MAX_KEYLENGTH);
+
+ok:
+       for_each_online_cpu(cpu)
+               if (&ctx->cword.encrypt == per_cpu(last_cword, cpu) ||
+                   &ctx->cword.decrypt == per_cpu(last_cword, cpu))
+                       per_cpu(last_cword, cpu) = NULL;
+
        return 0;
 }
 
 /* ====== Encryption/decryption routines ====== */
 
 /* These are the real call to PadLock. */
-static inline void padlock_reset_key(void)
+static inline void padlock_reset_key(struct cword *cword)
+{
+       int cpu = raw_smp_processor_id();
+
+       if (cword != per_cpu(last_cword, cpu))
+               asm volatile ("pushfl; popfl");
+}
+
+static inline void padlock_store_cword(struct cword *cword)
 {
-       asm volatile ("pushfl; popfl");
+       per_cpu(last_cword, raw_smp_processor_id()) = cword;
 }
 
 /*
@@ -149,7 +169,7 @@ static inline void padlock_reset_key(void)
  */
 
 static inline void padlock_xcrypt(const u8 *input, u8 *output, void *key,
-                                 void *control_word)
+                                 struct cword *control_word)
 {
        asm volatile (".byte 0xf3,0x0f,0xa7,0xc8"       /* rep xcryptecb */
                      : "+S"(input), "+D"(output)
@@ -213,22 +233,24 @@ static void aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
 {
        struct aes_ctx *ctx = aes_ctx(tfm);
        int ts_state;
-       padlock_reset_key();
 
+       padlock_reset_key(&ctx->cword.encrypt);
        ts_state = irq_ts_save();
        aes_crypt(in, out, ctx->E, &ctx->cword.encrypt);
        irq_ts_restore(ts_state);
+       padlock_store_cword(&ctx->cword.encrypt);
 }
 
 static void aes_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
 {
        struct aes_ctx *ctx = aes_ctx(tfm);
        int ts_state;
-       padlock_reset_key();
 
+       padlock_reset_key(&ctx->cword.encrypt);
        ts_state = irq_ts_save();
        aes_crypt(in, out, ctx->D, &ctx->cword.decrypt);
        irq_ts_restore(ts_state);
+       padlock_store_cword(&ctx->cword.encrypt);
 }
 
 static struct crypto_alg aes_alg = {
@@ -261,7 +283,7 @@ static int ecb_aes_encrypt(struct blkcipher_desc *desc,
        int err;
        int ts_state;
 
-       padlock_reset_key();
+       padlock_reset_key(&ctx->cword.encrypt);
 
        blkcipher_walk_init(&walk, dst, src, nbytes);
        err = blkcipher_walk_virt(desc, &walk);
@@ -276,6 +298,8 @@ static int ecb_aes_encrypt(struct blkcipher_desc *desc,
        }
        irq_ts_restore(ts_state);
 
+       padlock_store_cword(&ctx->cword.encrypt);
+
        return err;
 }
 
@@ -288,7 +312,7 @@ static int ecb_aes_decrypt(struct blkcipher_desc *desc,
        int err;
        int ts_state;
 
-       padlock_reset_key();
+       padlock_reset_key(&ctx->cword.decrypt);
 
        blkcipher_walk_init(&walk, dst, src, nbytes);
        err = blkcipher_walk_virt(desc, &walk);
@@ -302,6 +326,9 @@ static int ecb_aes_decrypt(struct blkcipher_desc *desc,
                err = blkcipher_walk_done(desc, &walk, nbytes);
        }
        irq_ts_restore(ts_state);
+
+       padlock_store_cword(&ctx->cword.encrypt);
+
        return err;
 }
 
@@ -336,7 +363,7 @@ static int cbc_aes_encrypt(struct blkcipher_desc *desc,
        int err;
        int ts_state;
 
-       padlock_reset_key();
+       padlock_reset_key(&ctx->cword.encrypt);
 
        blkcipher_walk_init(&walk, dst, src, nbytes);
        err = blkcipher_walk_virt(desc, &walk);
@@ -353,6 +380,8 @@ static int cbc_aes_encrypt(struct blkcipher_desc *desc,
        }
        irq_ts_restore(ts_state);
 
+       padlock_store_cword(&ctx->cword.decrypt);
+
        return err;
 }
 
@@ -365,7 +394,7 @@ static int cbc_aes_decrypt(struct blkcipher_desc *desc,
        int err;
        int ts_state;
 
-       padlock_reset_key();
+       padlock_reset_key(&ctx->cword.encrypt);
 
        blkcipher_walk_init(&walk, dst, src, nbytes);
        err = blkcipher_walk_virt(desc, &walk);
@@ -380,6 +409,9 @@ static int cbc_aes_decrypt(struct blkcipher_desc *desc,
        }
 
        irq_ts_restore(ts_state);
+
+       padlock_store_cword(&ctx->cword.encrypt);
+
        return err;
 }
 
index 2460766..a3918c1 100644 (file)
@@ -127,7 +127,6 @@ struct talitos_private {
 
        /* request callback tasklet */
        struct tasklet_struct done_task;
-       struct tasklet_struct error_task;
 
        /* list of registered algorithms */
        struct list_head alg_list;
@@ -138,6 +137,7 @@ struct talitos_private {
 
 /* .features flag */
 #define TALITOS_FTR_SRC_LINK_TBL_LEN_INCLUDES_EXTENT 0x00000001
+#define TALITOS_FTR_HW_AUTH_CHECK 0x00000002
 
 /*
  * map virtual single (contiguous) pointer to h/w descriptor pointer
@@ -184,6 +184,11 @@ static int reset_channel(struct device *dev, int ch)
        setbits32(priv->reg + TALITOS_CCCR_LO(ch), TALITOS_CCCR_LO_CDWE |
                  TALITOS_CCCR_LO_CDIE);
 
+       /* and ICCR writeback, if available */
+       if (priv->features & TALITOS_FTR_HW_AUTH_CHECK)
+               setbits32(priv->reg + TALITOS_CCCR_LO(ch),
+                         TALITOS_CCCR_LO_IWSE);
+
        return 0;
 }
 
@@ -239,6 +244,11 @@ static int init_device(struct device *dev)
        setbits32(priv->reg + TALITOS_IMR, TALITOS_IMR_INIT);
        setbits32(priv->reg + TALITOS_IMR_LO, TALITOS_IMR_LO_INIT);
 
+       /* disable integrity check error interrupts (use writeback instead) */
+       if (priv->features & TALITOS_FTR_HW_AUTH_CHECK)
+               setbits32(priv->reg + TALITOS_MDEUICR_LO,
+                         TALITOS_MDEUICR_LO_ICE);
+
        return 0;
 }
 
@@ -370,6 +380,12 @@ static void talitos_done(unsigned long data)
 
        for (ch = 0; ch < priv->num_channels; ch++)
                flush_channel(dev, ch, 0, 0);
+
+       /* At this point, all completed channels have been processed.
+        * Unmask done interrupts for channels completed later on.
+        */
+       setbits32(priv->reg + TALITOS_IMR, TALITOS_IMR_INIT);
+       setbits32(priv->reg + TALITOS_IMR_LO, TALITOS_IMR_LO_INIT);
 }
 
 /*
@@ -469,16 +485,13 @@ static void report_eu_error(struct device *dev, int ch, struct talitos_desc *des
 /*
  * recover from error interrupts
  */
-static void talitos_error(unsigned long data)
+static void talitos_error(unsigned long data, u32 isr, u32 isr_lo)
 {
        struct device *dev = (struct device *)data;
        struct talitos_private *priv = dev_get_drvdata(dev);
        unsigned int timeout = TALITOS_TIMEOUT;
        int ch, error, reset_dev = 0, reset_ch = 0;
-       u32 isr, isr_lo, v, v_lo;
-
-       isr = in_be32(priv->reg + TALITOS_ISR);
-       isr_lo = in_be32(priv->reg + TALITOS_ISR_LO);
+       u32 v, v_lo;
 
        for (ch = 0; ch < priv->num_channels; ch++) {
                /* skip channels without errors */
@@ -560,16 +573,19 @@ static irqreturn_t talitos_interrupt(int irq, void *data)
 
        isr = in_be32(priv->reg + TALITOS_ISR);
        isr_lo = in_be32(priv->reg + TALITOS_ISR_LO);
-
-       /* ack */
+       /* Acknowledge interrupt */
        out_be32(priv->reg + TALITOS_ICR, isr);
        out_be32(priv->reg + TALITOS_ICR_LO, isr_lo);
 
        if (unlikely((isr & ~TALITOS_ISR_CHDONE) || isr_lo))
-               talitos_error((unsigned long)data);
+               talitos_error((unsigned long)data, isr, isr_lo);
        else
-               if (likely(isr & TALITOS_ISR_CHDONE))
+               if (likely(isr & TALITOS_ISR_CHDONE)) {
+                       /* mask further done interrupts. */
+                       clrbits32(priv->reg + TALITOS_IMR, TALITOS_IMR_DONE);
+                       /* done_task will unmask done interrupts at exit */
                        tasklet_schedule(&priv->done_task);
+               }
 
        return (isr || isr_lo) ? IRQ_HANDLED : IRQ_NONE;
 }
@@ -802,7 +818,7 @@ static void ipsec_esp_encrypt_done(struct device *dev,
        aead_request_complete(areq, err);
 }
 
-static void ipsec_esp_decrypt_done(struct device *dev,
+static void ipsec_esp_decrypt_swauth_done(struct device *dev,
                                   struct talitos_desc *desc, void *context,
                                   int err)
 {
@@ -834,6 +850,27 @@ static void ipsec_esp_decrypt_done(struct device *dev,
        aead_request_complete(req, err);
 }
 
+static void ipsec_esp_decrypt_hwauth_done(struct device *dev,
+                                  struct talitos_desc *desc, void *context,
+                                  int err)
+{
+       struct aead_request *req = context;
+       struct ipsec_esp_edesc *edesc =
+                container_of(desc, struct ipsec_esp_edesc, desc);
+
+       ipsec_esp_unmap(dev, edesc, req);
+
+       /* check ICV auth status */
+       if (!err)
+               if ((desc->hdr_lo & DESC_HDR_LO_ICCR1_MASK) !=
+                   DESC_HDR_LO_ICCR1_PASS)
+                       err = -EBADMSG;
+
+       kfree(edesc);
+
+       aead_request_complete(req, err);
+}
+
 /*
  * convert scatterlist to SEC h/w link table format
  * stop at cryptlen bytes
@@ -887,6 +924,7 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
        unsigned int authsize = ctx->authsize;
        unsigned int ivsize;
        int sg_count, ret;
+       int sg_link_tbl_len;
 
        /* hmac key */
        map_single_talitos_ptr(dev, &desc->ptr[0], ctx->authkeylen, &ctx->key,
@@ -924,33 +962,19 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
        if (sg_count == 1) {
                desc->ptr[4].ptr = cpu_to_be32(sg_dma_address(areq->src));
        } else {
-               sg_count = sg_to_link_tbl(areq->src, sg_count, cryptlen,
+               sg_link_tbl_len = cryptlen;
+
+               if ((edesc->desc.hdr & DESC_HDR_MODE1_MDEU_CICV) &&
+                       (edesc->desc.hdr & DESC_HDR_MODE0_ENCRYPT) == 0) {
+                       sg_link_tbl_len = cryptlen + authsize;
+               }
+               sg_count = sg_to_link_tbl(areq->src, sg_count, sg_link_tbl_len,
                                          &edesc->link_tbl[0]);
                if (sg_count > 1) {
-                       struct talitos_ptr *link_tbl_ptr =
-                               &edesc->link_tbl[sg_count-1];
-                       struct scatterlist *sg;
-                       struct talitos_private *priv = dev_get_drvdata(dev);
-
                        desc->ptr[4].j_extent |= DESC_PTR_LNKTBL_JUMP;
                        desc->ptr[4].ptr = cpu_to_be32(edesc->dma_link_tbl);
                        dma_sync_single_for_device(ctx->dev, edesc->dma_link_tbl,
                                                   edesc->dma_len, DMA_BIDIRECTIONAL);
-                       /* If necessary for this SEC revision,
-                        * add a link table entry for ICV.
-                        */
-                       if ((priv->features &
-                            TALITOS_FTR_SRC_LINK_TBL_LEN_INCLUDES_EXTENT) &&
-                           (edesc->desc.hdr & DESC_HDR_MODE0_ENCRYPT) == 0) {
-                               link_tbl_ptr->j_extent = 0;
-                               link_tbl_ptr++;
-                               link_tbl_ptr->j_extent = DESC_PTR_LNKTBL_RETURN;
-                               link_tbl_ptr->len = cpu_to_be16(authsize);
-                               sg = sg_last(areq->src, edesc->src_nents ? : 1);
-                               link_tbl_ptr->ptr = cpu_to_be32(
-                                               (char *)sg_dma_address(sg)
-                                               + sg->length - authsize);
-                       }
                } else {
                        /* Only one segment now, so no link tbl needed */
                        desc->ptr[4].ptr = cpu_to_be32(sg_dma_address(areq->src));
@@ -975,13 +999,9 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
                desc->ptr[5].ptr = cpu_to_be32((struct talitos_ptr *)
                                               edesc->dma_link_tbl +
                                               edesc->src_nents + 1);
-               if (areq->src == areq->dst) {
-                       memcpy(link_tbl_ptr, &edesc->link_tbl[0],
-                              edesc->src_nents * sizeof(struct talitos_ptr));
-               } else {
-                       sg_count = sg_to_link_tbl(areq->dst, sg_count, cryptlen,
-                                                 link_tbl_ptr);
-               }
+               sg_count = sg_to_link_tbl(areq->dst, sg_count, cryptlen,
+                                         link_tbl_ptr);
+
                /* Add an entry to the link table for ICV data */
                link_tbl_ptr += sg_count - 1;
                link_tbl_ptr->j_extent = 0;
@@ -1106,11 +1126,14 @@ static int aead_authenc_encrypt(struct aead_request *req)
        return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_encrypt_done);
 }
 
+
+
 static int aead_authenc_decrypt(struct aead_request *req)
 {
        struct crypto_aead *authenc = crypto_aead_reqtfm(req);
        struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
        unsigned int authsize = ctx->authsize;
+       struct talitos_private *priv = dev_get_drvdata(ctx->dev);
        struct ipsec_esp_edesc *edesc;
        struct scatterlist *sg;
        void *icvdata;
@@ -1122,22 +1145,39 @@ static int aead_authenc_decrypt(struct aead_request *req)
        if (IS_ERR(edesc))
                return PTR_ERR(edesc);
 
-       /* stash incoming ICV for later cmp with ICV generated by the h/w */
-       if (edesc->dma_len)
-               icvdata = &edesc->link_tbl[edesc->src_nents +
-                                          edesc->dst_nents + 2];
-       else
-               icvdata = &edesc->link_tbl[0];
+       if ((priv->features & TALITOS_FTR_HW_AUTH_CHECK) &&
+           (((!edesc->src_nents && !edesc->dst_nents) ||
+               priv->features & TALITOS_FTR_SRC_LINK_TBL_LEN_INCLUDES_EXTENT))) {
+
+               /* decrypt and check the ICV */
+               edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND |
+                                 DESC_HDR_MODE1_MDEU_CICV;
+
+               /* reset integrity check result bits */
+               edesc->desc.hdr_lo = 0;
+
+               return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_decrypt_hwauth_done);
+
+       } else {
+
+               /* Have to check the ICV with software */
 
-       sg = sg_last(req->src, edesc->src_nents ? : 1);
+               edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND;
+
+               /* stash incoming ICV for later cmp with ICV generated by the h/w */
+               if (edesc->dma_len)
+                       icvdata = &edesc->link_tbl[edesc->src_nents +
+                                                  edesc->dst_nents + 2];
+               else
+                       icvdata = &edesc->link_tbl[0];
 
-       memcpy(icvdata, (char *)sg_virt(sg) + sg->length - ctx->authsize,
-              ctx->authsize);
+               sg = sg_last(req->src, edesc->src_nents ? : 1);
 
-       /* decrypt */
-       edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND;
+               memcpy(icvdata, (char *)sg_virt(sg) + sg->length - ctx->authsize,
+                      ctx->authsize);
 
-       return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_decrypt_done);
+               return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_decrypt_swauth_done);
+       }
 }
 
 static int aead_authenc_givencrypt(
@@ -1391,7 +1431,6 @@ static int talitos_remove(struct of_device *ofdev)
        }
 
        tasklet_kill(&priv->done_task);
-       tasklet_kill(&priv->error_task);
 
        iounmap(priv->reg);
 
@@ -1451,10 +1490,9 @@ static int talitos_probe(struct of_device *ofdev,
 
        priv->ofdev = ofdev;
 
-       INIT_LIST_HEAD(&priv->alg_list);
-
        tasklet_init(&priv->done_task, talitos_done, (unsigned long)dev);
-       tasklet_init(&priv->error_task, talitos_error, (unsigned long)dev);
+
+       INIT_LIST_HEAD(&priv->alg_list);
 
        priv->irq = irq_of_parse_and_map(np, 0);
 
@@ -1508,6 +1546,9 @@ static int talitos_probe(struct of_device *ofdev,
        if (of_device_is_compatible(np, "fsl,sec3.0"))
                priv->features |= TALITOS_FTR_SRC_LINK_TBL_LEN_INCLUDES_EXTENT;
 
+       if (of_device_is_compatible(np, "fsl,sec2.1"))
+               priv->features |= TALITOS_FTR_HW_AUTH_CHECK;
+
        priv->head_lock = kmalloc(sizeof(spinlock_t) * priv->num_channels,
                                  GFP_KERNEL);
        priv->tail_lock = kmalloc(sizeof(spinlock_t) * priv->num_channels,
@@ -1551,7 +1592,7 @@ static int talitos_probe(struct of_device *ofdev,
                goto err_out;
        }
        for (i = 0; i < priv->num_channels; i++)
-               atomic_set(&priv->submit_count[i], -priv->chfifo_len);
+               atomic_set(&priv->submit_count[i], -(priv->chfifo_len - 1));
 
        priv->head = kzalloc(sizeof(int) * priv->num_channels, GFP_KERNEL);
        priv->tail = kzalloc(sizeof(int) * priv->num_channels, GFP_KERNEL);
index c48a405..575981f 100644 (file)
@@ -37,7 +37,8 @@
 #define TALITOS_MCR_LO                 0x1038
 #define   TALITOS_MCR_SWR              0x1     /* s/w reset */
 #define TALITOS_IMR                    0x1008  /* interrupt mask register */
-#define   TALITOS_IMR_INIT             0x10fff /* enable channel IRQs */
+#define   TALITOS_IMR_INIT             0x100ff /* enable channel IRQs */
+#define   TALITOS_IMR_DONE             0x00055 /* done IRQs */
 #define TALITOS_IMR_LO                 0x100C
 #define   TALITOS_IMR_LO_INIT          0x20000 /* allow RNGU error IRQs */
 #define TALITOS_ISR                    0x1010  /* interrupt status register */
@@ -55,6 +56,7 @@
 #define   TALITOS_CCCR_CONT            0x2    /* channel continue */
 #define   TALITOS_CCCR_RESET           0x1    /* channel reset */
 #define TALITOS_CCCR_LO(ch)            (ch * TALITOS_CH_STRIDE + 0x110c)
+#define   TALITOS_CCCR_LO_IWSE         0x80   /* chan. ICCR writeback enab. */
 #define   TALITOS_CCCR_LO_CDWE         0x10   /* chan. done writeback enab. */
 #define   TALITOS_CCCR_LO_NT           0x4    /* notification type */
 #define   TALITOS_CCCR_LO_CDIE         0x2    /* channel done IRQ enable */
 #define TALITOS_AESUISR_LO             0x4034
 #define TALITOS_MDEUISR                        0x6030 /* message digest unit */
 #define TALITOS_MDEUISR_LO             0x6034
+#define TALITOS_MDEUICR                        0x6038 /* interrupt control */
+#define TALITOS_MDEUICR_LO             0x603c
+#define   TALITOS_MDEUICR_LO_ICE       0x4000 /* integrity check IRQ enable */
 #define TALITOS_AFEUISR                        0x8030 /* arc4 unit */
 #define TALITOS_AFEUISR_LO             0x8034
 #define TALITOS_RNGUISR                        0xa030 /* random number unit */
  */
 
 /* written back when done */
-#define DESC_HDR_DONE                  __constant_cpu_to_be32(0xff000000)
+#define DESC_HDR_DONE                  cpu_to_be32(0xff000000)
+#define DESC_HDR_LO_ICCR1_MASK         cpu_to_be32(0x00180000)
+#define DESC_HDR_LO_ICCR1_PASS         cpu_to_be32(0x00080000)
+#define DESC_HDR_LO_ICCR1_FAIL         cpu_to_be32(0x00100000)
 
 /* primary execution unit select */
-#define        DESC_HDR_SEL0_MASK              __constant_cpu_to_be32(0xf0000000)
-#define        DESC_HDR_SEL0_AFEU              __constant_cpu_to_be32(0x10000000)
-#define        DESC_HDR_SEL0_DEU               __constant_cpu_to_be32(0x20000000)
-#define        DESC_HDR_SEL0_MDEUA             __constant_cpu_to_be32(0x30000000)
-#define        DESC_HDR_SEL0_MDEUB             __constant_cpu_to_be32(0xb0000000)
-#define        DESC_HDR_SEL0_RNG               __constant_cpu_to_be32(0x40000000)
-#define        DESC_HDR_SEL0_PKEU              __constant_cpu_to_be32(0x50000000)
-#define        DESC_HDR_SEL0_AESU              __constant_cpu_to_be32(0x60000000)
-#define        DESC_HDR_SEL0_KEU               __constant_cpu_to_be32(0x70000000)
-#define        DESC_HDR_SEL0_CRCU              __constant_cpu_to_be32(0x80000000)
+#define        DESC_HDR_SEL0_MASK              cpu_to_be32(0xf0000000)
+#define        DESC_HDR_SEL0_AFEU              cpu_to_be32(0x10000000)
+#define        DESC_HDR_SEL0_DEU               cpu_to_be32(0x20000000)
+#define        DESC_HDR_SEL0_MDEUA             cpu_to_be32(0x30000000)
+#define        DESC_HDR_SEL0_MDEUB             cpu_to_be32(0xb0000000)
+#define        DESC_HDR_SEL0_RNG               cpu_to_be32(0x40000000)
+#define        DESC_HDR_SEL0_PKEU              cpu_to_be32(0x50000000)
+#define        DESC_HDR_SEL0_AESU              cpu_to_be32(0x60000000)
+#define        DESC_HDR_SEL0_KEU               cpu_to_be32(0x70000000)
+#define        DESC_HDR_SEL0_CRCU              cpu_to_be32(0x80000000)
 
 /* primary execution unit mode (MODE0) and derivatives */
-#define        DESC_HDR_MODE0_ENCRYPT          __constant_cpu_to_be32(0x00100000)
-#define        DESC_HDR_MODE0_AESU_CBC         __constant_cpu_to_be32(0x00200000)
-#define        DESC_HDR_MODE0_DEU_CBC          __constant_cpu_to_be32(0x00400000)
-#define        DESC_HDR_MODE0_DEU_3DES         __constant_cpu_to_be32(0x00200000)
-#define        DESC_HDR_MODE0_MDEU_INIT        __constant_cpu_to_be32(0x01000000)
-#define        DESC_HDR_MODE0_MDEU_HMAC        __constant_cpu_to_be32(0x00800000)
-#define        DESC_HDR_MODE0_MDEU_PAD         __constant_cpu_to_be32(0x00400000)
-#define        DESC_HDR_MODE0_MDEU_MD5         __constant_cpu_to_be32(0x00200000)
-#define        DESC_HDR_MODE0_MDEU_SHA256      __constant_cpu_to_be32(0x00100000)
-#define        DESC_HDR_MODE0_MDEU_SHA1        __constant_cpu_to_be32(0x00000000)
+#define        DESC_HDR_MODE0_ENCRYPT          cpu_to_be32(0x00100000)
+#define        DESC_HDR_MODE0_AESU_CBC         cpu_to_be32(0x00200000)
+#define        DESC_HDR_MODE0_DEU_CBC          cpu_to_be32(0x00400000)
+#define        DESC_HDR_MODE0_DEU_3DES         cpu_to_be32(0x00200000)
+#define        DESC_HDR_MODE0_MDEU_INIT        cpu_to_be32(0x01000000)
+#define        DESC_HDR_MODE0_MDEU_HMAC        cpu_to_be32(0x00800000)
+#define        DESC_HDR_MODE0_MDEU_PAD         cpu_to_be32(0x00400000)
+#define        DESC_HDR_MODE0_MDEU_MD5         cpu_to_be32(0x00200000)
+#define        DESC_HDR_MODE0_MDEU_SHA256      cpu_to_be32(0x00100000)
+#define        DESC_HDR_MODE0_MDEU_SHA1        cpu_to_be32(0x00000000)
 #define        DESC_HDR_MODE0_MDEU_MD5_HMAC    (DESC_HDR_MODE0_MDEU_MD5 | \
                                         DESC_HDR_MODE0_MDEU_HMAC)
 #define        DESC_HDR_MODE0_MDEU_SHA256_HMAC (DESC_HDR_MODE0_MDEU_SHA256 | \
                                         DESC_HDR_MODE0_MDEU_HMAC)
 
 /* secondary execution unit select (SEL1) */
-#define        DESC_HDR_SEL1_MASK              __constant_cpu_to_be32(0x000f0000)
-#define        DESC_HDR_SEL1_MDEUA             __constant_cpu_to_be32(0x00030000)
-#define        DESC_HDR_SEL1_MDEUB             __constant_cpu_to_be32(0x000b0000)
-#define        DESC_HDR_SEL1_CRCU              __constant_cpu_to_be32(0x00080000)
+#define        DESC_HDR_SEL1_MASK              cpu_to_be32(0x000f0000)
+#define        DESC_HDR_SEL1_MDEUA             cpu_to_be32(0x00030000)
+#define        DESC_HDR_SEL1_MDEUB             cpu_to_be32(0x000b0000)
+#define        DESC_HDR_SEL1_CRCU              cpu_to_be32(0x00080000)
 
 /* secondary execution unit mode (MODE1) and derivatives */
-#define        DESC_HDR_MODE1_MDEU_INIT        __constant_cpu_to_be32(0x00001000)
-#define        DESC_HDR_MODE1_MDEU_HMAC        __constant_cpu_to_be32(0x00000800)
-#define        DESC_HDR_MODE1_MDEU_PAD         __constant_cpu_to_be32(0x00000400)
-#define        DESC_HDR_MODE1_MDEU_MD5         __constant_cpu_to_be32(0x00000200)
-#define        DESC_HDR_MODE1_MDEU_SHA256      __constant_cpu_to_be32(0x00000100)
-#define        DESC_HDR_MODE1_MDEU_SHA1        __constant_cpu_to_be32(0x00000000)
+#define        DESC_HDR_MODE1_MDEU_CICV        cpu_to_be32(0x00004000)
+#define        DESC_HDR_MODE1_MDEU_INIT        cpu_to_be32(0x00001000)
+#define        DESC_HDR_MODE1_MDEU_HMAC        cpu_to_be32(0x00000800)
+#define        DESC_HDR_MODE1_MDEU_PAD         cpu_to_be32(0x00000400)
+#define        DESC_HDR_MODE1_MDEU_MD5         cpu_to_be32(0x00000200)
+#define        DESC_HDR_MODE1_MDEU_SHA256      cpu_to_be32(0x00000100)
+#define        DESC_HDR_MODE1_MDEU_SHA1        cpu_to_be32(0x00000000)
 #define        DESC_HDR_MODE1_MDEU_MD5_HMAC    (DESC_HDR_MODE1_MDEU_MD5 | \
                                         DESC_HDR_MODE1_MDEU_HMAC)
 #define        DESC_HDR_MODE1_MDEU_SHA256_HMAC (DESC_HDR_MODE1_MDEU_SHA256 | \
                                         DESC_HDR_MODE1_MDEU_HMAC)
 
 /* direction of overall data flow (DIR) */
-#define        DESC_HDR_DIR_INBOUND            __constant_cpu_to_be32(0x00000002)
+#define        DESC_HDR_DIR_INBOUND            cpu_to_be32(0x00000002)
 
 /* request done notification (DN) */
-#define        DESC_HDR_DONE_NOTIFY            __constant_cpu_to_be32(0x00000001)
+#define        DESC_HDR_DONE_NOTIFY            cpu_to_be32(0x00000001)
 
 /* descriptor types */
-#define DESC_HDR_TYPE_AESU_CTR_NONSNOOP                __constant_cpu_to_be32(0 << 3)
-#define DESC_HDR_TYPE_IPSEC_ESP                        __constant_cpu_to_be32(1 << 3)
-#define DESC_HDR_TYPE_COMMON_NONSNOOP_NO_AFEU  __constant_cpu_to_be32(2 << 3)
-#define DESC_HDR_TYPE_HMAC_SNOOP_NO_AFEU       __constant_cpu_to_be32(4 << 3)
+#define DESC_HDR_TYPE_AESU_CTR_NONSNOOP                cpu_to_be32(0 << 3)
+#define DESC_HDR_TYPE_IPSEC_ESP                        cpu_to_be32(1 << 3)
+#define DESC_HDR_TYPE_COMMON_NONSNOOP_NO_AFEU  cpu_to_be32(2 << 3)
+#define DESC_HDR_TYPE_HMAC_SNOOP_NO_AFEU       cpu_to_be32(4 << 3)
 
 /* link table extent field bits */
 #define DESC_PTR_LNKTBL_JUMP                   0x80
index 3384a71..6c3d60b 100644 (file)
@@ -160,9 +160,39 @@ struct sh_mobile_i2c_data {
 
 static void activate_ch(struct sh_mobile_i2c_data *pd)
 {
+       unsigned long i2c_clk;
+       u_int32_t num;
+       u_int32_t denom;
+       u_int32_t tmp;
+
        /* Make sure the clock is enabled */
        clk_enable(pd->clk);
 
+       /* Get clock rate after clock is enabled */
+       i2c_clk = clk_get_rate(pd->clk);
+
+       /* Calculate the value for iccl. From the data sheet:
+        * iccl = (p clock / transfer rate) * (L / (L + H))
+        * where L and H are the SCL low/high ratio (5/4 in this case).
+        * We also round off the result.
+        */
+       num = i2c_clk * 5;
+       denom = NORMAL_SPEED * 9;
+       tmp = num * 10 / denom;
+       if (tmp % 10 >= 5)
+               pd->iccl = (u_int8_t)((num/denom) + 1);
+       else
+               pd->iccl = (u_int8_t)(num/denom);
+
+       /* Calculate the value for icch. From the data sheet:
+          icch = (p clock / transfer rate) * (H / (L + H)) */
+       num = i2c_clk * 4;
+       tmp = num * 10 / denom;
+       if (tmp % 10 >= 5)
+               pd->icch = (u_int8_t)((num/denom) + 1);
+       else
+               pd->icch = (u_int8_t)(num/denom);
+
        /* Enable channel and configure rx ack */
        iowrite8(ioread8(ICCR(pd)) | ICCR_ICE, ICCR(pd));
 
@@ -459,40 +489,6 @@ static struct i2c_algorithm sh_mobile_i2c_algorithm = {
        .master_xfer    = sh_mobile_i2c_xfer,
 };
 
-static void sh_mobile_i2c_setup_channel(struct platform_device *dev)
-{
-       struct sh_mobile_i2c_data *pd = platform_get_drvdata(dev);
-       unsigned long peripheral_clk = clk_get_rate(pd->clk);
-       u_int32_t num;
-       u_int32_t denom;
-       u_int32_t tmp;
-
-       spin_lock_init(&pd->lock);
-       init_waitqueue_head(&pd->wait);
-
-       /* Calculate the value for iccl. From the data sheet:
-        * iccl = (p clock / transfer rate) * (L / (L + H))
-        * where L and H are the SCL low/high ratio (5/4 in this case).
-        * We also round off the result.
-        */
-       num = peripheral_clk * 5;
-       denom = NORMAL_SPEED * 9;
-       tmp = num * 10 / denom;
-       if (tmp % 10 >= 5)
-               pd->iccl = (u_int8_t)((num/denom) + 1);
-       else
-               pd->iccl = (u_int8_t)(num/denom);
-
-       /* Calculate the value for icch. From the data sheet:
-          icch = (p clock / transfer rate) * (H / (L + H)) */
-       num = peripheral_clk * 4;
-       tmp = num * 10 / denom;
-       if (tmp % 10 >= 5)
-               pd->icch = (u_int8_t)((num/denom) + 1);
-       else
-               pd->icch = (u_int8_t)(num/denom);
-}
-
 static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, int hook)
 {
        struct resource *res;
@@ -533,6 +529,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
        struct sh_mobile_i2c_data *pd;
        struct i2c_adapter *adap;
        struct resource *res;
+       char clk_name[8];
        int size;
        int ret;
 
@@ -542,9 +539,10 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
                return -ENOMEM;
        }
 
-       pd->clk = clk_get(&dev->dev, "peripheral_clk");
+       snprintf(clk_name, sizeof(clk_name), "i2c%d", dev->id);
+       pd->clk = clk_get(&dev->dev, clk_name);
        if (IS_ERR(pd->clk)) {
-               dev_err(&dev->dev, "cannot get peripheral clock\n");
+               dev_err(&dev->dev, "cannot get clock \"%s\"\n", clk_name);
                ret = PTR_ERR(pd->clk);
                goto err;
        }
@@ -586,7 +584,8 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
 
        strlcpy(adap->name, dev->name, sizeof(adap->name));
 
-       sh_mobile_i2c_setup_channel(dev);
+       spin_lock_init(&pd->lock);
+       init_waitqueue_head(&pd->wait);
 
        ret = i2c_add_numbered_adapter(adap);
        if (ret < 0) {
index c600ab7..5c8a1bc 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/delay.h>
 #include <linux/platform_device.h>
 #include <linux/input.h>
+#include <linux/clk.h>
 #include <linux/io.h>
 #include <asm/sh_keysc.h>
 
@@ -39,6 +40,7 @@ static const struct {
 
 struct sh_keysc_priv {
        void __iomem *iomem_base;
+       struct clk *clk;
        unsigned long last_keys;
        struct input_dev *input;
        struct sh_keysc_info pdata;
@@ -125,6 +127,7 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
        struct sh_keysc_info *pdata;
        struct resource *res;
        struct input_dev *input;
+       char clk_name[8];
        int i, k;
        int irq, error;
 
@@ -165,11 +168,19 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
                goto err1;
        }
 
+       snprintf(clk_name, sizeof(clk_name), "keysc%d", pdev->id);
+       priv->clk = clk_get(&pdev->dev, clk_name);
+       if (IS_ERR(priv->clk)) {
+               dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+               error = PTR_ERR(priv->clk);
+               goto err2;
+       }
+
        priv->input = input_allocate_device();
        if (!priv->input) {
                dev_err(&pdev->dev, "failed to allocate input device\n");
                error = -ENOMEM;
-               goto err2;
+               goto err3;
        }
 
        input = priv->input;
@@ -187,7 +198,7 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
        error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev);
        if (error) {
                dev_err(&pdev->dev, "failed to request IRQ\n");
-               goto err3;
+               goto err4;
        }
 
        for (i = 0; i < SH_KEYSC_MAXKEYS; i++) {
@@ -199,18 +210,22 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
        error = input_register_device(input);
        if (error) {
                dev_err(&pdev->dev, "failed to register input device\n");
-               goto err4;
+               goto err5;
        }
 
+       clk_enable(priv->clk);
+
        iowrite16((sh_keysc_mode[pdata->mode].kymd << 8) |
                  pdata->scan_timing, priv->iomem_base + KYCR1_OFFS);
        iowrite16(0, priv->iomem_base + KYOUTDR_OFFS);
        iowrite16(KYCR2_IRQ_LEVEL, priv->iomem_base + KYCR2_OFFS);
        return 0;
- err4:
+ err5:
        free_irq(irq, pdev);
- err3:
+ err4:
        input_free_device(input);
+ err3:
+       clk_put(priv->clk);
  err2:
        iounmap(priv->iomem_base);
  err1:
@@ -230,6 +245,9 @@ static int __devexit sh_keysc_remove(struct platform_device *pdev)
        free_irq(platform_get_irq(pdev, 0), pdev);
        iounmap(priv->iomem_base);
 
+       clk_disable(priv->clk);
+       clk_put(priv->clk);
+
        platform_set_drvdata(pdev, NULL);
        kfree(priv);
        return 0;
index 550e80f..0aa66ec 100644 (file)
@@ -156,8 +156,8 @@ void capifs_new_ncci(unsigned int number, dev_t device)
        if (!inode)
                return;
        inode->i_ino = number+2;
-       inode->i_uid = config.setuid ? config.uid : current->fsuid;
-       inode->i_gid = config.setgid ? config.gid : current->fsgid;
+       inode->i_uid = config.setuid ? config.uid : current_fsuid();
+       inode->i_gid = config.setgid ? config.gid : current_fsgid();
        inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
        init_special_inode(inode, S_IFCHR|config.mode, device);
        //inode->i_op = &capifs_file_inode_operations;
index 484299b..8f9f491 100644 (file)
@@ -246,7 +246,8 @@ hysdn_conf_open(struct inode *ino, struct file *filep)
        }
        if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
                hysdn_addlog(card, "config open for uid=%d gid=%d mode=0x%x",
-                            filep->f_uid, filep->f_gid, filep->f_mode);
+                            filep->f_cred->fsuid, filep->f_cred->fsgid,
+                            filep->f_mode);
 
        if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
                /* write only access -> write boot file or conf line */
@@ -331,7 +332,8 @@ hysdn_conf_close(struct inode *ino, struct file *filep)
        }
        if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
                hysdn_addlog(card, "config close for uid=%d gid=%d mode=0x%x",
-                            filep->f_uid, filep->f_gid, filep->f_mode);
+                            filep->f_cred->fsuid, filep->f_cred->fsgid,
+                            filep->f_mode);
 
        if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
                /* write only access -> write boot file or conf line */
index 47102c2..057fd7e 100644 (file)
@@ -759,7 +759,7 @@ config VIDEO_PXA27x
 
 config VIDEO_SH_MOBILE_CEU
        tristate "SuperH Mobile CEU Interface driver"
-       depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA
+       depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK
        select VIDEOBUF_DMA_CONTIG
        ---help---
          This is a v4l2 driver for the SuperH Mobile CEU Interface
index 2407607..536b1a9 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/platform_device.h>
 #include <linux/mutex.h>
 #include <linux/videodev2.h>
+#include <linux/clk.h>
 
 #include <media/v4l2-common.h>
 #include <media/v4l2-dev.h>
@@ -89,6 +90,7 @@ struct sh_mobile_ceu_dev {
 
        unsigned int irq;
        void __iomem *base;
+       struct clk *clk;
        unsigned long video_limit;
 
        /* lock used to protect videobuf */
@@ -309,6 +311,8 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
        if (ret)
                goto err;
 
+       clk_enable(pcdev->clk);
+
        ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
        while (ceu_read(pcdev, CSTSR) & 1)
                msleep(1);
@@ -342,6 +346,8 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
        }
        spin_unlock_irqrestore(&pcdev->lock, flags);
 
+       clk_disable(pcdev->clk);
+
        icd->ops->release(icd);
 
        dev_info(&icd->dev,
@@ -550,6 +556,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
        struct sh_mobile_ceu_dev *pcdev;
        struct resource *res;
        void __iomem *base;
+       char clk_name[8];
        unsigned int irq;
        int err = 0;
 
@@ -615,6 +622,14 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
                goto exit_release_mem;
        }
 
+       snprintf(clk_name, sizeof(clk_name), "ceu%d", pdev->id);
+       pcdev->clk = clk_get(&pdev->dev, clk_name);
+       if (IS_ERR(pcdev->clk)) {
+               dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+               err = PTR_ERR(pcdev->clk);
+               goto exit_free_irq;
+       }
+
        pcdev->ici.priv = pcdev;
        pcdev->ici.dev.parent = &pdev->dev;
        pcdev->ici.nr = pdev->id;
@@ -623,10 +638,12 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
 
        err = soc_camera_host_register(&pcdev->ici);
        if (err)
-               goto exit_free_irq;
+               goto exit_free_clk;
 
        return 0;
 
+exit_free_clk:
+       clk_put(pcdev->clk);
 exit_free_irq:
        free_irq(pcdev->irq, pcdev);
 exit_release_mem:
@@ -645,6 +662,7 @@ static int sh_mobile_ceu_remove(struct platform_device *pdev)
        struct sh_mobile_ceu_dev *pcdev = platform_get_drvdata(pdev);
 
        soc_camera_host_unregister(&pcdev->ici);
+       clk_put(pcdev->clk);
        free_irq(pcdev->irq, pcdev);
        if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
                dma_release_declared_memory(&pdev->dev);
index 33b6d1b..55dc70c 100644 (file)
@@ -702,6 +702,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
        struct tun_net *tn;
        struct tun_struct *tun;
        struct net_device *dev;
+       const struct cred *cred = current_cred();
        int err;
 
        tn = net_generic(net, tun_net_id);
@@ -712,11 +713,12 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
 
                /* Check permissions */
                if (((tun->owner != -1 &&
-                     current->euid != tun->owner) ||
+                     cred->euid != tun->owner) ||
                     (tun->group != -1 &&
-                     current->egid != tun->group)) &&
-                    !capable(CAP_NET_ADMIN))
+                     cred->egid != tun->group)) &&
+                   !capable(CAP_NET_ADMIN)) {
                        return -EPERM;
+               }
        }
        else if (__dev_get_by_name(net, ifr->ifr_name))
                return -EINVAL;
index 165fc01..557b54a 100644 (file)
@@ -51,7 +51,6 @@
 #ifdef CONFIG_SUPERH
 #include <asm/clock.h>
 #include <asm/sh_bios.h>
-#include <asm/kgdb.h>
 #endif
 
 #include "sh-sci.h"
@@ -65,10 +64,6 @@ struct sci_port {
        /* Port IRQs: ERI, RXI, TXI, BRI (optional) */
        unsigned int            irqs[SCIx_NR_IRQS];
 
-       /* Port pin configuration */
-       void                    (*init_pins)(struct uart_port *port,
-                                            unsigned int cflag);
-
        /* Port enable callback */
        void                    (*enable)(struct uart_port *port);
 
@@ -85,10 +80,6 @@ struct sci_port {
 #endif
 };
 
-#ifdef CONFIG_SH_KGDB
-static struct sci_port *kgdb_sci_port;
-#endif
-
 #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
 static struct sci_port *serial_console_port;
 #endif
@@ -101,21 +92,26 @@ static void sci_stop_tx(struct uart_port *port);
 static struct sci_port sci_ports[SCI_NPORTS];
 static struct uart_driver sci_uart_driver;
 
-#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && \
-    defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
+static inline struct sci_port *
+to_sci_port(struct uart_port *uart)
+{
+       return container_of(uart, struct sci_port, port);
+}
+
+#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE)
+
+#ifdef CONFIG_CONSOLE_POLL
 static inline void handle_error(struct uart_port *port)
 {
        /* Clear error flags */
        sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
 }
 
-static int get_char(struct uart_port *port)
+static int sci_poll_get_char(struct uart_port *port)
 {
-       unsigned long flags;
        unsigned short status;
        int c;
 
-       spin_lock_irqsave(&port->lock, flags);
        do {
                status = sci_in(port, SCxSR);
                if (status & SCxSR_ERRORS(port)) {
@@ -123,23 +119,21 @@ static int get_char(struct uart_port *port)
                        continue;
                }
        } while (!(status & SCxSR_RDxF(port)));
+
        c = sci_in(port, SCxRDR);
-       sci_in(port, SCxSR);            /* Dummy read */
+
+       /* Dummy read */
+       sci_in(port, SCxSR);
        sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
-       spin_unlock_irqrestore(&port->lock, flags);
 
        return c;
 }
-#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
+#endif
 
-#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) || defined(CONFIG_SH_KGDB)
-static void put_char(struct uart_port *port, char c)
+static void sci_poll_put_char(struct uart_port *port, unsigned char c)
 {
-       unsigned long flags;
        unsigned short status;
 
-       spin_lock_irqsave(&port->lock, flags);
-
        do {
                status = sci_in(port, SCxSR);
        } while (!(status & SCxSR_TDxE(port)));
@@ -147,96 +141,22 @@ static void put_char(struct uart_port *port, char c)
        sci_in(port, SCxSR);            /* Dummy read */
        sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
        sci_out(port, SCxTDR, c);
-
-       spin_unlock_irqrestore(&port->lock, flags);
 }
-#endif
-
-#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
-static void put_string(struct sci_port *sci_port, const char *buffer, int count)
-{
-       struct uart_port *port = &sci_port->port;
-       const unsigned char *p = buffer;
-       int i;
-
-#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
-       int checksum;
-       int usegdb=0;
-
-#ifdef CONFIG_SH_STANDARD_BIOS
-       /* This call only does a trap the first time it is
-        * called, and so is safe to do here unconditionally
-        */
-       usegdb |= sh_bios_in_gdb_mode();
-#endif
-#ifdef CONFIG_SH_KGDB
-       usegdb |= (kgdb_in_gdb_mode && (sci_port == kgdb_sci_port));
-#endif
-
-       if (usegdb) {
-           /*  $<packet info>#<checksum>. */
-           do {
-               unsigned char c;
-               put_char(port, '$');
-               put_char(port, 'O'); /* 'O'utput to console */
-               checksum = 'O';
-
-               for (i=0; i<count; i++) { /* Don't use run length encoding */
-                       int h, l;
-
-                       c = *p++;
-                       h = hex_asc_hi(c);
-                       l = hex_asc_lo(c);
-                       put_char(port, h);
-                       put_char(port, l);
-                       checksum += h + l;
-               }
-               put_char(port, '#');
-               put_char(port, hex_asc_hi(checksum));
-               put_char(port, hex_asc_lo(checksum));
-           } while  (get_char(port) != '+');
-       } else
-#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
-       for (i=0; i<count; i++) {
-               if (*p == 10)
-                       put_char(port, '\r');
-               put_char(port, *p++);
-       }
-}
-#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */
-
-#ifdef CONFIG_SH_KGDB
-static int kgdb_sci_getchar(void)
-{
-        int c;
-
-        /* Keep trying to read a character, this could be neater */
-        while ((c = get_char(&kgdb_sci_port->port)) < 0)
-               cpu_relax();
-
-        return c;
-}
-
-static inline void kgdb_sci_putchar(int c)
-{
-        put_char(&kgdb_sci_port->port, c);
-}
-#endif /* CONFIG_SH_KGDB */
+#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */
 
 #if defined(__H8300S__)
 enum { sci_disable, sci_enable };
 
-static void h8300_sci_config(struct uart_portport, unsigned int ctrl)
+static void h8300_sci_config(struct uart_port *port, unsigned int ctrl)
 {
-       volatile unsigned char *mstpcrl=(volatile unsigned char *)MSTPCRL;
+       volatile unsigned char *mstpcrl = (volatile unsigned char *)MSTPCRL;
        int ch = (port->mapbase  - SMR0) >> 3;
        unsigned char mask = 1 << (ch+1);
 
-       if (ctrl == sci_disable) {
+       if (ctrl == sci_disable)
                *mstpcrl |= mask;
-       } else {
+       else
                *mstpcrl &= ~mask;
-       }
 }
 
 static inline void h8300_sci_enable(struct uart_port *port)
@@ -251,7 +171,7 @@ static inline void h8300_sci_disable(struct uart_port *port)
 #endif
 
 #if defined(__H8300H__) || defined(__H8300S__)
-static void sci_init_pins_sci(struct uart_port* port, unsigned int cflag)
+static void sci_init_pins(struct uart_port *port, unsigned int cflag)
 {
        int ch = (port->mapbase - SMR0) >> 3;
 
@@ -266,141 +186,99 @@ static void sci_init_pins_sci(struct uart_port* port, unsigned int cflag)
        /* tx mark output*/
        H8300_SCI_DR(ch) |= h8300_sci_pins[ch].tx;
 }
-#else
-#define sci_init_pins_sci NULL
-#endif
-
-#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
-static void sci_init_pins_irda(struct uart_port *port, unsigned int cflag)
+#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712)
+static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
 {
-       unsigned int fcr_val = 0;
-
-       if (cflag & CRTSCTS)
-               fcr_val |= SCFCR_MCE;
-
-       sci_out(port, SCFCR, fcr_val);
-}
-#else
-#define sci_init_pins_irda NULL
-#endif
-
-#if defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712)
-static void sci_init_pins_scif(struct uart_port* port, unsigned int cflag)
-{
-       unsigned int fcr_val = 0;
-
-       set_sh771x_scif_pfc(port);
-       if (cflag & CRTSCTS) {
-               fcr_val |= SCFCR_MCE;
-       }
-       sci_out(port, SCFCR, fcr_val);
+       if (port->mapbase == 0xA4400000) {
+               __raw_writew(__raw_readw(PACR) & 0xffc0, PACR);
+               __raw_writew(__raw_readw(PBCR) & 0x0fff, PBCR);
+       } else if (port->mapbase == 0xA4410000)
+               __raw_writew(__raw_readw(PBCR) & 0xf003, PBCR);
 }
 #elif defined(CONFIG_CPU_SUBTYPE_SH7720) || defined(CONFIG_CPU_SUBTYPE_SH7721)
-static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
+static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
 {
-       unsigned int fcr_val = 0;
        unsigned short data;
 
        if (cflag & CRTSCTS) {
                /* enable RTS/CTS */
                if (port->mapbase == 0xa4430000) { /* SCIF0 */
                        /* Clear PTCR bit 9-2; enable all scif pins but sck */
-                       data = ctrl_inw(PORT_PTCR);
-                       ctrl_outw((data & 0xfc03), PORT_PTCR);
+                       data = __raw_readw(PORT_PTCR);
+                       __raw_writew((data & 0xfc03), PORT_PTCR);
                } else if (port->mapbase == 0xa4438000) { /* SCIF1 */
                        /* Clear PVCR bit 9-2 */
-                       data = ctrl_inw(PORT_PVCR);
-                       ctrl_outw((data & 0xfc03), PORT_PVCR);
+                       data = __raw_readw(PORT_PVCR);
+                       __raw_writew((data & 0xfc03), PORT_PVCR);
                }
-               fcr_val |= SCFCR_MCE;
        } else {
                if (port->mapbase == 0xa4430000) { /* SCIF0 */
                        /* Clear PTCR bit 5-2; enable only tx and rx  */
-                       data = ctrl_inw(PORT_PTCR);
-                       ctrl_outw((data & 0xffc3), PORT_PTCR);
+                       data = __raw_readw(PORT_PTCR);
+                       __raw_writew((data & 0xffc3), PORT_PTCR);
                } else if (port->mapbase == 0xa4438000) { /* SCIF1 */
                        /* Clear PVCR bit 5-2 */
-                       data = ctrl_inw(PORT_PVCR);
-                       ctrl_outw((data & 0xffc3), PORT_PVCR);
+                       data = __raw_readw(PORT_PVCR);
+                       __raw_writew((data & 0xffc3), PORT_PVCR);
                }
        }
-       sci_out(port, SCFCR, fcr_val);
 }
 #elif defined(CONFIG_CPU_SH3)
 /* For SH7705, SH7706, SH7707, SH7709, SH7709A, SH7729 */
-static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
+static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
 {
-       unsigned int fcr_val = 0;
        unsigned short data;
 
        /* We need to set SCPCR to enable RTS/CTS */
-       data = ctrl_inw(SCPCR);
+       data = __raw_readw(SCPCR);
        /* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/
-       ctrl_outw(data & 0x0fcf, SCPCR);
+       __raw_writew(data & 0x0fcf, SCPCR);
 
-       if (cflag & CRTSCTS)
-               fcr_val |= SCFCR_MCE;
-       else {
+       if (!(cflag & CRTSCTS)) {
                /* We need to set SCPCR to enable RTS/CTS */
-               data = ctrl_inw(SCPCR);
+               data = __raw_readw(SCPCR);
                /* Clear out SCP7MD1,0, SCP4MD1,0,
                   Set SCP6MD1,0 = {01} (output)  */
-               ctrl_outw((data & 0x0fcf) | 0x1000, SCPCR);
+               __raw_writew((data & 0x0fcf) | 0x1000, SCPCR);
 
                data = ctrl_inb(SCPDR);
                /* Set /RTS2 (bit6) = 0 */
                ctrl_outb(data & 0xbf, SCPDR);
        }
-
-       sci_out(port, SCFCR, fcr_val);
 }
 #elif defined(CONFIG_CPU_SUBTYPE_SH7722)
-static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
+static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
 {
-       unsigned int fcr_val = 0;
        unsigned short data;
 
        if (port->mapbase == 0xffe00000) {
-               data = ctrl_inw(PSCR);
+               data = __raw_readw(PSCR);
                data &= ~0x03cf;
-               if (cflag & CRTSCTS)
-                       fcr_val |= SCFCR_MCE;
-               else
+               if (!(cflag & CRTSCTS))
                        data |= 0x0340;
 
-               ctrl_outw(data, PSCR);
+               __raw_writew(data, PSCR);
        }
-       /* SCIF1 and SCIF2 should be setup by board code */
-
-       sci_out(port, SCFCR, fcr_val);
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7723)
-static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
-{
-       /* Nothing to do here.. */
-       sci_out(port, SCFCR, 0);
 }
-#else
-/* For SH7750 */
-static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
-{
-       unsigned int fcr_val = 0;
-
-       if (cflag & CRTSCTS) {
-               fcr_val |= SCFCR_MCE;
-       } else {
-#if defined(CONFIG_CPU_SUBTYPE_SH7343) || defined(CONFIG_CPU_SUBTYPE_SH7366)
-               /* Nothing */
 #elif defined(CONFIG_CPU_SUBTYPE_SH7763) || \
       defined(CONFIG_CPU_SUBTYPE_SH7780) || \
       defined(CONFIG_CPU_SUBTYPE_SH7785) || \
       defined(CONFIG_CPU_SUBTYPE_SHX3)
-               ctrl_outw(0x0080, SCSPTR0); /* Set RTS = 1 */
+static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
+{
+       if (!(cflag & CRTSCTS))
+               __raw_writew(0x0080, SCSPTR0); /* Set RTS = 1 */
+}
+#elif defined(CONFIG_CPU_SH4) && !defined(CONFIG_CPU_SH4A)
+static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
+{
+       if (!(cflag & CRTSCTS))
+               __raw_writew(0x0080, SCSPTR2); /* Set RTS = 1 */
+}
 #else
-               ctrl_outw(0x0080, SCSPTR2); /* Set RTS = 1 */
-#endif
-       }
-       sci_out(port, SCFCR, fcr_val);
+static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
+{
+       /* Nothing to do */
 }
 #endif
 
@@ -419,18 +297,26 @@ static inline int scif_rxroom(struct uart_port *port)
 #elif defined(CONFIG_CPU_SUBTYPE_SH7763)
 static inline int scif_txroom(struct uart_port *port)
 {
-       if((port->mapbase == 0xffe00000) || (port->mapbase == 0xffe08000)) /* SCIF0/1*/
+       if ((port->mapbase == 0xffe00000) ||
+           (port->mapbase == 0xffe08000)) {
+               /* SCIF0/1*/
                return SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0xff);
-       else /* SCIF2 */
+       } else {
+               /* SCIF2 */
                return SCIF2_TXROOM_MAX - (sci_in(port, SCFDR) >> 8);
+       }
 }
 
 static inline int scif_rxroom(struct uart_port *port)
 {
-       if((port->mapbase == 0xffe00000) || (port->mapbase == 0xffe08000)) /* SCIF0/1*/
+       if ((port->mapbase == 0xffe00000) ||
+           (port->mapbase == 0xffe08000)) {
+               /* SCIF0/1*/
                return sci_in(port, SCRFDR) & 0xff;
-       else /* SCIF2 */
+       } else {
+               /* SCIF2 */
                return sci_in(port, SCFDR) & SCIF2_RFDC_MASK;
+       }
 }
 #else
 static inline int scif_txroom(struct uart_port *port)
@@ -446,12 +332,12 @@ static inline int scif_rxroom(struct uart_port *port)
 
 static inline int sci_txroom(struct uart_port *port)
 {
-       return ((sci_in(port, SCxSR) & SCI_TDRE) != 0);
+       return (sci_in(port, SCxSR) & SCI_TDRE) != 0;
 }
 
 static inline int sci_rxroom(struct uart_port *port)
 {
-       return ((sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0);
+       return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0;
 }
 
 /* ********************************************************************** *
@@ -469,11 +355,10 @@ static void sci_transmit_chars(struct uart_port *port)
        status = sci_in(port, SCxSR);
        if (!(status & SCxSR_TDxE(port))) {
                ctrl = sci_in(port, SCSCR);
-               if (uart_circ_empty(xmit)) {
+               if (uart_circ_empty(xmit))
                        ctrl &= ~SCI_CTRL_FLAGS_TIE;
-               } else {
+               else
                        ctrl |= SCI_CTRL_FLAGS_TIE;
-               }
                sci_out(port, SCSCR, ctrl);
                return;
        }
@@ -521,11 +406,11 @@ static void sci_transmit_chars(struct uart_port *port)
 }
 
 /* On SH3, SCIF may read end-of-break as a space->mark char */
-#define STEPFN(c)  ({int __c=(c); (((__c-1)|(__c)) == -1); })
+#define STEPFN(c)  ({int __c = (c); (((__c-1)|(__c)) == -1); })
 
 static inline void sci_receive_chars(struct uart_port *port)
 {
-       struct sci_port *sci_port = (struct sci_port *)port;
+       struct sci_port *sci_port = to_sci_port(port);
        struct tty_struct *tty = port->info->port.tty;
        int i, count, copied = 0;
        unsigned short status;
@@ -550,13 +435,13 @@ static inline void sci_receive_chars(struct uart_port *port)
 
                if (port->type == PORT_SCI) {
                        char c = sci_in(port, SCxRDR);
-                       if (uart_handle_sysrq_char(port, c) || sci_port->break_flag)
+                       if (uart_handle_sysrq_char(port, c) ||
+                           sci_port->break_flag)
                                count = 0;
-                       else {
+                       else
                                tty_insert_flip_char(tty, c, TTY_NORMAL);
-                       }
                } else {
-                       for (i=0; i<count; i++) {
+                       for (i = 0; i < count; i++) {
                                char c = sci_in(port, SCxRDR);
                                status = sci_in(port, SCxSR);
 #if defined(CONFIG_CPU_SH3)
@@ -569,7 +454,7 @@ static inline void sci_receive_chars(struct uart_port *port)
                                        }
 
                                        /* Nonzero => end-of-break */
-                                       pr_debug("scif: debounce<%02x>\n", c);
+                                       dev_dbg(port->dev, "debounce<%02x>\n", c);
                                        sci_port->break_flag = 0;
 
                                        if (STEPFN(c)) {
@@ -586,12 +471,13 @@ static inline void sci_receive_chars(struct uart_port *port)
                                /* Store data and status */
                                if (status&SCxSR_FER(port)) {
                                        flag = TTY_FRAME;
-                                       pr_debug("sci: frame error\n");
+                                       dev_notice(port->dev, "frame error\n");
                                } else if (status&SCxSR_PER(port)) {
                                        flag = TTY_PARITY;
-                                       pr_debug("sci: parity error\n");
+                                       dev_notice(port->dev, "parity error\n");
                                } else
                                        flag = TTY_NORMAL;
+
                                tty_insert_flip_char(tty, c, flag);
                        }
                }
@@ -651,13 +537,14 @@ static inline int sci_handle_errors(struct uart_port *port)
                /* overrun error */
                if (tty_insert_flip_char(tty, 0, TTY_OVERRUN))
                        copied++;
-               pr_debug("sci: overrun error\n");
+
+               dev_notice(port->dev, "overrun error");
        }
 
        if (status & SCxSR_FER(port)) {
                if (sci_rxd_in(port) == 0) {
                        /* Notify of BREAK */
-                       struct sci_port *sci_port = (struct sci_port *)port;
+                       struct sci_port *sci_port = to_sci_port(port);
 
                        if (!sci_port->break_flag) {
                                sci_port->break_flag = 1;
@@ -666,15 +553,19 @@ static inline int sci_handle_errors(struct uart_port *port)
                                /* Do sysrq handling. */
                                if (uart_handle_break(port))
                                        return 0;
-                               pr_debug("sci: BREAK detected\n");
+
+                               dev_dbg(port->dev, "BREAK detected\n");
+
                                if (tty_insert_flip_char(tty, 0, TTY_BREAK))
-                                       copied++;
-                       }
+                                       copied++;
+                       }
+
                } else {
                        /* frame error */
                        if (tty_insert_flip_char(tty, 0, TTY_FRAME))
                                copied++;
-                       pr_debug("sci: frame error\n");
+
+                       dev_notice(port->dev, "frame error\n");
                }
        }
 
@@ -682,7 +573,8 @@ static inline int sci_handle_errors(struct uart_port *port)
                /* parity error */
                if (tty_insert_flip_char(tty, 0, TTY_PARITY))
                        copied++;
-               pr_debug("sci: parity error\n");
+
+               dev_notice(port->dev, "parity error");
        }
 
        if (copied)
@@ -691,6 +583,27 @@ static inline int sci_handle_errors(struct uart_port *port)
        return copied;
 }
 
+static inline int sci_handle_fifo_overrun(struct uart_port *port)
+{
+       struct tty_struct *tty = port->info->port.tty;
+       int copied = 0;
+
+       if (port->type != PORT_SCIF)
+               return 0;
+
+       if ((sci_in(port, SCLSR) & SCIF_ORER) != 0) {
+               sci_out(port, SCLSR, 0);
+
+               tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+               tty_flip_buffer_push(tty);
+
+               dev_notice(port->dev, "overrun error\n");
+               copied++;
+       }
+
+       return copied;
+}
+
 static inline int sci_handle_breaks(struct uart_port *port)
 {
        int copied = 0;
@@ -709,23 +622,15 @@ static inline int sci_handle_breaks(struct uart_port *port)
                /* Notify of BREAK */
                if (tty_insert_flip_char(tty, 0, TTY_BREAK))
                        copied++;
-               pr_debug("sci: BREAK detected\n");
-       }
 
-#if defined(SCIF_ORER)
-       /* XXX: Handle SCIF overrun error */
-       if (port->type != PORT_SCI && (sci_in(port, SCLSR) & SCIF_ORER) != 0) {
-               sci_out(port, SCLSR, 0);
-               if (tty_insert_flip_char(tty, 0, TTY_OVERRUN)) {
-                       copied++;
-                       pr_debug("sci: overrun error\n");
-               }
+               dev_dbg(port->dev, "BREAK detected\n");
        }
-#endif
 
        if (copied)
                tty_flip_buffer_push(tty);
 
+       copied += sci_handle_fifo_overrun(port);
+
        return copied;
 }
 
@@ -763,16 +668,7 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr)
                        sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
                }
        } else {
-#if defined(SCIF_ORER)
-               if((sci_in(port, SCLSR) & SCIF_ORER) != 0) {
-                       struct tty_struct *tty = port->info->port.tty;
-
-                       sci_out(port, SCLSR, 0);
-                       tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-                       tty_flip_buffer_push(tty);
-                       pr_debug("scif: overrun error\n");
-               }
-#endif
+               sci_handle_fifo_overrun(port);
                sci_rx_interrupt(irq, ptr);
        }
 
@@ -801,8 +697,8 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
        struct uart_port *port = ptr;
        irqreturn_t ret = IRQ_NONE;
 
-        ssr_status = sci_in(port,SCxSR);
-        scr_status = sci_in(port,SCSCR);
+       ssr_status = sci_in(port, SCxSR);
+       scr_status = sci_in(port, SCSCR);
 
        /* Tx Interrupt */
        if ((ssr_status & 0x0020) && (scr_status & SCI_CTRL_FLAGS_TIE))
@@ -820,7 +716,7 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
        return ret;
 }
 
-#if defined(CONFIG_CPU_FREQ) && defined(CONFIG_HAVE_CLK)
+#ifdef CONFIG_HAVE_CLK
 /*
  * Here we define a transistion notifier so that we can update all of our
  * ports' baud rate when the peripheral clock changes.
@@ -828,41 +724,20 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
 static int sci_notifier(struct notifier_block *self,
                        unsigned long phase, void *p)
 {
-       struct cpufreq_freqs *freqs = p;
        int i;
 
        if ((phase == CPUFREQ_POSTCHANGE) ||
-           (phase == CPUFREQ_RESUMECHANGE)){
+           (phase == CPUFREQ_RESUMECHANGE))
                for (i = 0; i < SCI_NPORTS; i++) {
-                       struct uart_port *port = &sci_ports[i].port;
-                       struct clk *clk;
-
-                       /*
-                        * Update the uartclk per-port if frequency has
-                        * changed, since it will no longer necessarily be
-                        * consistent with the old frequency.
-                        *
-                        * Really we want to be able to do something like
-                        * uart_change_speed() or something along those lines
-                        * here to implicitly reset the per-port baud rate..
-                        *
-                        * Clean this up later..
-                        */
-                       clk = clk_get(NULL, "module_clk");
-                       port->uartclk = clk_get_rate(clk);
-                       clk_put(clk);
+                       struct sci_port *s = &sci_ports[i];
+                       s->port.uartclk = clk_get_rate(s->clk);
                }
 
-               printk(KERN_INFO "%s: got a postchange notification "
-                      "for cpu %d (old %d, new %d)\n",
-                      __func__, freqs->cpu, freqs->old, freqs->new);
-       }
-
        return NOTIFY_OK;
 }
 
 static struct notifier_block sci_nb = { &sci_notifier, NULL, 0 };
-#endif /* CONFIG_CPU_FREQ && CONFIG_HAVE_CLK */
+#endif
 
 static int sci_request_irq(struct sci_port *port)
 {
@@ -875,23 +750,22 @@ static int sci_request_irq(struct sci_port *port)
                               "SCI Transmit Data Empty", "SCI Break" };
 
        if (port->irqs[0] == port->irqs[1]) {
-               if (!port->irqs[0]) {
-                       printk(KERN_ERR "sci: Cannot allocate irq.(IRQ=0)\n");
+               if (unlikely(!port->irqs[0]))
                        return -ENODEV;
-               }
 
                if (request_irq(port->irqs[0], sci_mpxed_interrupt,
                                IRQF_DISABLED, "sci", port)) {
-                       printk(KERN_ERR "sci: Cannot allocate irq.\n");
+                       dev_err(port->port.dev, "Can't allocate IRQ\n");
                        return -ENODEV;
                }
        } else {
                for (i = 0; i < ARRAY_SIZE(handlers); i++) {
-                       if (!port->irqs[i])
+                       if (unlikely(!port->irqs[i]))
                                continue;
+
                        if (request_irq(port->irqs[i], handlers[i],
                                        IRQF_DISABLED, desc[i], port)) {
-                               printk(KERN_ERR "sci: Cannot allocate irq.\n");
+                               dev_err(port->port.dev, "Can't allocate IRQ\n");
                                return -ENODEV;
                        }
                }
@@ -904,12 +778,9 @@ static void sci_free_irq(struct sci_port *port)
 {
        int i;
 
-        if (port->irqs[0] == port->irqs[1]) {
-                if (!port->irqs[0])
-                        printk("sci: sci_free_irq error\n");
-               else
-                        free_irq(port->irqs[0], port);
-        } else {
+       if (port->irqs[0] == port->irqs[1])
+               free_irq(port->irqs[0], port);
+       else {
                for (i = 0; i < ARRAY_SIZE(port->irqs); i++) {
                        if (!port->irqs[i])
                                continue;
@@ -1028,7 +899,6 @@ static void sci_shutdown(struct uart_port *port)
 static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
                            struct ktermios *old)
 {
-       struct sci_port *s = &sci_ports[port->line];
        unsigned int status, baud, smr_val;
        int t = -1;
 
@@ -1060,32 +930,36 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
        sci_out(port, SCSMR, smr_val);
 
        if (t > 0) {
-               if(t >= 256) {
+               if (t >= 256) {
                        sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1);
                        t >>= 2;
-               } else {
+               } else
                        sci_out(port, SCSMR, sci_in(port, SCSMR) & ~3);
-               }
+
                sci_out(port, SCBRR, t);
                udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */
        }
 
-       if (likely(s->init_pins))
-               s->init_pins(port, termios->c_cflag);
+       sci_init_pins(port, termios->c_cflag);
+       sci_out(port, SCFCR, (termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0);
 
        sci_out(port, SCSCR, SCSCR_INIT(port));
 
        if ((termios->c_cflag & CREAD) != 0)
-              sci_start_rx(port,0);
+               sci_start_rx(port, 0);
 }
 
 static const char *sci_type(struct uart_port *port)
 {
        switch (port->type) {
-               case PORT_SCI:  return "sci";
-               case PORT_SCIF: return "scif";
-               case PORT_IRDA: return "irda";
-               case PORT_SCIFA:        return "scifa";
+       case PORT_IRDA:
+               return "irda";
+       case PORT_SCI:
+               return "sci";
+       case PORT_SCIF:
+               return "scif";
+       case PORT_SCIFA:
+               return "scifa";
        }
 
        return NULL;
@@ -1108,19 +982,6 @@ static void sci_config_port(struct uart_port *port, int flags)
 
        port->type = s->type;
 
-       switch (port->type) {
-       case PORT_SCI:
-               s->init_pins = sci_init_pins_sci;
-               break;
-       case PORT_SCIF:
-       case PORT_SCIFA:
-               s->init_pins = sci_init_pins_scif;
-               break;
-       case PORT_IRDA:
-               s->init_pins = sci_init_pins_irda;
-               break;
-       }
-
        if (port->flags & UPF_IOREMAP && !port->membase) {
 #if defined(CONFIG_SUPERH64)
                port->mapbase = onchip_remap(SCIF_ADDR_SH5, 1024, "SCIF");
@@ -1129,7 +990,7 @@ static void sci_config_port(struct uart_port *port, int flags)
                port->membase = ioremap_nocache(port->mapbase, 0x40);
 #endif
 
-               printk(KERN_ERR "sci: can't remap port#%d\n", port->line);
+               dev_err(port->dev, "can't remap port#%d\n", port->line);
        }
 }
 
@@ -1163,6 +1024,10 @@ static struct uart_ops sci_uart_ops = {
        .request_port   = sci_request_port,
        .config_port    = sci_config_port,
        .verify_port    = sci_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+       .poll_get_char  = sci_poll_get_char,
+       .poll_put_char  = sci_poll_put_char,
+#endif
 };
 
 static void __init sci_init_ports(void)
@@ -1229,7 +1094,15 @@ int __init early_sci_setup(struct uart_port *port)
 static void serial_console_write(struct console *co, const char *s,
                                 unsigned count)
 {
-       put_string(serial_console_port, s, count);
+       struct uart_port *port = &serial_console_port->port;
+       int i;
+
+       for (i = 0; i < count; i++) {
+               if (*s == 10)
+                       sci_poll_put_char(port, '\r');
+
+               sci_poll_put_char(port, *s++);
+       }
 }
 
 static int __init serial_console_setup(struct console *co, char *options)
@@ -1307,89 +1180,8 @@ static int __init sci_console_init(void)
 console_initcall(sci_console_init);
 #endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */
 
-#ifdef CONFIG_SH_KGDB_CONSOLE
-/*
- * FIXME: Most of this can go away.. at the moment, we rely on
- * arch/sh/kernel/setup.c to do the command line parsing for kgdb, though
- * most of that can easily be done here instead.
- *
- * For the time being, just accept the values that were parsed earlier..
- */
-static void __init kgdb_console_get_options(struct uart_port *port, int *baud,
-                                           int *parity, int *bits)
-{
-       *baud = kgdb_baud;
-       *parity = tolower(kgdb_parity);
-       *bits = kgdb_bits - '0';
-}
-
-/*
- * The naming here is somewhat misleading, since kgdb_console_setup() takes
- * care of the early-on initialization for kgdb, regardless of whether we
- * actually use kgdb as a console or not.
- *
- * On the plus side, this lets us kill off the old kgdb_sci_setup() nonsense.
- */
-int __init kgdb_console_setup(struct console *co, char *options)
-{
-       struct uart_port *port = &sci_ports[kgdb_portnum].port;
-       int baud = 38400;
-       int bits = 8;
-       int parity = 'n';
-       int flow = 'n';
-
-       if (co->index != kgdb_portnum)
-               co->index = kgdb_portnum;
-
-       kgdb_sci_port = &sci_ports[co->index];
-       port = &kgdb_sci_port->port;
-
-       /*
-        * Also need to check port->type, we don't actually have any
-        * UPIO_PORT ports, but uart_report_port() handily misreports
-        * it anyways if we don't have a port available by the time this is
-        * called.
-        */
-       if (!port->type)
-               return -ENODEV;
-       if (!port->membase || !port->mapbase)
-               return -ENODEV;
-
-       if (options)
-               uart_parse_options(options, &baud, &parity, &bits, &flow);
-       else
-               kgdb_console_get_options(port, &baud, &parity, &bits);
-
-       kgdb_getchar = kgdb_sci_getchar;
-       kgdb_putchar = kgdb_sci_putchar;
-
-       return uart_set_options(port, co, baud, parity, bits, flow);
-}
-
-static struct console kgdb_console = {
-       .name           = "ttySC",
-       .device         = uart_console_device,
-       .write          = kgdb_console_write,
-       .setup          = kgdb_console_setup,
-       .flags          = CON_PRINTBUFFER,
-       .index          = -1,
-       .data           = &sci_uart_driver,
-};
-
-/* Register the KGDB console so we get messages (d'oh!) */
-static int __init kgdb_console_init(void)
-{
-       sci_init_ports();
-       register_console(&kgdb_console);
-       return 0;
-}
-console_initcall(kgdb_console_init);
-#endif /* CONFIG_SH_KGDB_CONSOLE */
-
-#if defined(CONFIG_SH_KGDB_CONSOLE)
-#define SCI_CONSOLE    &kgdb_console
-#elif defined(CONFIG_SERIAL_SH_SCI_CONSOLE)
-#define SCI_CONSOLE    &serial_console
+#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE)
+#define SCI_CONSOLE    (&serial_console)
 #else
 #define SCI_CONSOLE    0
 #endif
@@ -1463,15 +1255,8 @@ static int __devinit sci_probe(struct platform_device *dev)
                uart_add_one_port(&sci_uart_driver, &sciport->port);
        }
 
-#if defined(CONFIG_SH_KGDB) && !defined(CONFIG_SH_KGDB_CONSOLE)
-       kgdb_sci_port   = &sci_ports[kgdb_portnum];
-       kgdb_getchar    = kgdb_sci_getchar;
-       kgdb_putchar    = kgdb_sci_putchar;
-#endif
-
-#if defined(CONFIG_CPU_FREQ) && defined(CONFIG_HAVE_CLK)
+#ifdef CONFIG_HAVE_CLK
        cpufreq_register_notifier(&sci_nb, CPUFREQ_TRANSITION_NOTIFIER);
-       dev_info(&dev->dev, "CPU frequency notifier registered\n");
 #endif
 
 #ifdef CONFIG_SH_STANDARD_BIOS
@@ -1491,6 +1276,10 @@ static int __devexit sci_remove(struct platform_device *dev)
 {
        int i;
 
+#ifdef CONFIG_HAVE_CLK
+       cpufreq_unregister_notifier(&sci_nb, CPUFREQ_TRANSITION_NOTIFIER);
+#endif
+
        for (i = 0; i < SCI_NPORTS; i++)
                uart_remove_one_port(&sci_uart_driver, &sci_ports[i].port);
 
index 9f33b06..38c600c 100644 (file)
 # define SCSPTR5       0xffef0024      /* 16 bit SCIF */
 # define SCIF_OPER     0x0001          /* Overrun error bit */
 # define SCSCR_INIT(port)      0x3a    /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7203) || \
+#elif defined(CONFIG_CPU_SUBTYPE_SH7201) || \
+      defined(CONFIG_CPU_SUBTYPE_SH7203) || \
       defined(CONFIG_CPU_SUBTYPE_SH7206) || \
       defined(CONFIG_CPU_SUBTYPE_SH7263)
 # define SCSPTR0 0xfffe8020 /* 16 bit SCIF */
 # define SCSPTR1 0xfffe8820 /* 16 bit SCIF */
 # define SCSPTR2 0xfffe9020 /* 16 bit SCIF */
 # define SCSPTR3 0xfffe9820 /* 16 bit SCIF */
+# if defined(CONFIG_CPU_SUBTYPE_SH7201)
+#  define SCSPTR4 0xfffeA020 /* 16 bit SCIF */
+#  define SCSPTR5 0xfffeA820 /* 16 bit SCIF */
+#  define SCSPTR6 0xfffeB020 /* 16 bit SCIF */
+#  define SCSPTR7 0xfffeB820 /* 16 bit SCIF */
+# endif
 # define SCSCR_INIT(port)      0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
 #elif defined(CONFIG_CPU_SUBTYPE_SH7619)
 # define SCSPTR0 0xf8400020 /* 16 bit SCIF */
 # define SCIF_TXROOM_MAX 16
 #endif
 
+#ifndef SCIF_ORER
+#define SCIF_ORER      0x0000
+#endif
+
 #define SCxSR_TEND(port)       (((port)->type == PORT_SCI) ? SCI_TEND   : SCIF_TEND)
 #define SCxSR_ERRORS(port)     (((port)->type == PORT_SCI) ? SCI_ERRORS : SCIF_ERRORS)
 #define SCxSR_RDxF(port)       (((port)->type == PORT_SCI) ? SCI_RDRF   : SCIF_RDF)
 #define SCxSR_FER(port)                (((port)->type == PORT_SCI) ? SCI_FER    : SCIF_FER)
 #define SCxSR_PER(port)                (((port)->type == PORT_SCI) ? SCI_PER    : SCIF_PER)
 #define SCxSR_BRK(port)                (((port)->type == PORT_SCI) ? 0x00       : SCIF_BRK)
-
-#if defined(CONFIG_CPU_SUBTYPE_SH7705)
-# define SCxSR_ORER(port)      (((port)->type == PORT_SCI) ? SCI_ORER : SCIF_ORER)
-#else
-# define SCxSR_ORER(port)      (((port)->type == PORT_SCI) ? SCI_ORER : 0x0000)
-#endif
+#define SCxSR_ORER(port)       (((port)->type == PORT_SCI) ? SCI_ORER   : SCIF_ORER)
 
 #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \
     defined(CONFIG_CPU_SUBTYPE_SH7720) || \
@@ -501,18 +507,6 @@ static inline int sci_rxd_in(struct uart_port *port)
 {
          return sci_in(port,SCxSR)&0x0010 ? 1 : 0;
 }
-static inline void set_sh771x_scif_pfc(struct uart_port *port)
-{
-       if (port->mapbase == 0xA4400000){
-               ctrl_outw(ctrl_inw(PACR)&0xffc0,PACR);
-               ctrl_outw(ctrl_inw(PBCR)&0x0fff,PBCR);
-               return;
-       }
-       if (port->mapbase == 0xA4410000){
-               ctrl_outw(ctrl_inw(PBCR)&0xf003,PBCR);
-               return;
-       }
-}
 #elif defined(CONFIG_CPU_SUBTYPE_SH7720) || \
       defined(CONFIG_CPU_SUBTYPE_SH7721)
 static inline int sci_rxd_in(struct uart_port *port)
@@ -664,7 +658,8 @@ static inline int sci_rxd_in(struct uart_port *port)
                return ctrl_inw(SCSPTR5) & 0x0001 ? 1 : 0; /* SCIF */
        return 1;
 }
-#elif defined(CONFIG_CPU_SUBTYPE_SH7203) || \
+#elif defined(CONFIG_CPU_SUBTYPE_SH7201) || \
+      defined(CONFIG_CPU_SUBTYPE_SH7203) || \
       defined(CONFIG_CPU_SUBTYPE_SH7206) || \
       defined(CONFIG_CPU_SUBTYPE_SH7263)
 static inline int sci_rxd_in(struct uart_port *port)
@@ -677,6 +672,16 @@ static inline int sci_rxd_in(struct uart_port *port)
                return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
        if (port->mapbase == 0xfffe9800)
                return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */
+#if defined(CONFIG_CPU_SUBTYPE_SH7201)
+       if (port->mapbase == 0xfffeA000)
+               return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
+       if (port->mapbase == 0xfffeA800)
+               return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
+       if (port->mapbase == 0xfffeB000)
+               return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
+       if (port->mapbase == 0xfffeB800)
+               return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */
+#endif
        return 1;
 }
 #elif defined(CONFIG_CPU_SUBTYPE_SH7619)
index 2bccefe..aa79280 100644 (file)
@@ -574,6 +574,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
 {
        struct usb_device *dev = NULL;
        struct dev_state *ps;
+       const struct cred *cred = current_cred();
        int ret;
 
        lock_kernel();
@@ -617,8 +618,8 @@ static int usbdev_open(struct inode *inode, struct file *file)
        init_waitqueue_head(&ps->wait);
        ps->discsignr = 0;
        ps->disc_pid = get_pid(task_pid(current));
-       ps->disc_uid = current->uid;
-       ps->disc_euid = current->euid;
+       ps->disc_uid = cred->uid;
+       ps->disc_euid = cred->euid;
        ps->disccontext = NULL;
        ps->ifclaimed = 0;
        security_task_getsecid(current, &ps->secid);
@@ -967,6 +968,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
        struct usb_host_endpoint *ep;
        struct async *as;
        struct usb_ctrlrequest *dr = NULL;
+       const struct cred *cred = current_cred();
        unsigned int u, totlen, isofrmlen;
        int ret, ifnum = -1;
        int is_in;
@@ -1174,8 +1176,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
        as->signr = uurb->signr;
        as->ifnum = ifnum;
        as->pid = get_pid(task_pid(current));
-       as->uid = current->uid;
-       as->euid = current->euid;
+       as->uid = cred->uid;
+       as->euid = cred->euid;
        security_task_getsecid(current, &as->secid);
        if (!is_in) {
                if (copy_from_user(as->urb->transfer_buffer, uurb->buffer,
index 9463226..185be76 100644 (file)
@@ -277,8 +277,8 @@ static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t de
 
        if (inode) {
                inode->i_mode = mode;
-               inode->i_uid = current->fsuid;
-               inode->i_gid = current->fsgid;
+               inode->i_uid = current_fsuid();
+               inode->i_gid = current_fsgid();
                inode->i_blocks = 0;
                inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
                switch (mode & S_IFMT) {
index 77b44fb..3a8879e 100644 (file)
@@ -623,7 +623,6 @@ static void start_ep0(struct m66592_ep *ep, struct m66592_request *req)
 #if defined(CONFIG_SUPERH_BUILT_IN_M66592)
 static void init_controller(struct m66592 *m66592)
 {
-       usbf_start_clock();
        m66592_bset(m66592, M66592_HSE, M66592_SYSCFG);         /* High spd */
        m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG);
        m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
@@ -671,9 +670,7 @@ static void init_controller(struct m66592 *m66592)
 
 static void disable_controller(struct m66592 *m66592)
 {
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592)
-       usbf_stop_clock();
-#else
+#if !defined(CONFIG_SUPERH_BUILT_IN_M66592)
        m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG);
        udelay(1);
        m66592_bclr(m66592, M66592_PLLC, M66592_SYSCFG);
@@ -686,9 +683,7 @@ static void disable_controller(struct m66592 *m66592)
 
 static void m66592_start_xclock(struct m66592 *m66592)
 {
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592)
-       usbf_start_clock();
-#else
+#if !defined(CONFIG_SUPERH_BUILT_IN_M66592)
        u16 tmp;
 
        tmp = m66592_read(m66592, M66592_SYSCFG);
@@ -1539,7 +1534,10 @@ static int __exit m66592_remove(struct platform_device *pdev)
        iounmap(m66592->reg);
        free_irq(platform_get_irq(pdev, 0), m66592);
        m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
-       usbf_stop_clock();
+#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
+       clk_disable(m66592->clk);
+       clk_put(m66592->clk);
+#endif
        kfree(m66592);
        return 0;
 }
@@ -1556,6 +1554,9 @@ static int __init m66592_probe(struct platform_device *pdev)
        int irq;
        void __iomem *reg = NULL;
        struct m66592 *m66592 = NULL;
+#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
+       char clk_name[8];
+#endif
        int ret = 0;
        int i;
 
@@ -1614,6 +1615,16 @@ static int __init m66592_probe(struct platform_device *pdev)
                goto clean_up;
        }
 
+#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
+       snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id);
+       m66592->clk = clk_get(&pdev->dev, clk_name);
+       if (IS_ERR(m66592->clk)) {
+               dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+               ret = PTR_ERR(m66592->clk);
+               goto clean_up2;
+       }
+       clk_enable(m66592->clk);
+#endif
        INIT_LIST_HEAD(&m66592->gadget.ep_list);
        m66592->gadget.ep0 = &m66592->ep[0].ep;
        INIT_LIST_HEAD(&m66592->gadget.ep0->ep_list);
@@ -1645,7 +1656,7 @@ static int __init m66592_probe(struct platform_device *pdev)
 
        m66592->ep0_req = m66592_alloc_request(&m66592->ep[0].ep, GFP_KERNEL);
        if (m66592->ep0_req == NULL)
-               goto clean_up2;
+               goto clean_up3;
        m66592->ep0_req->complete = nop_completion;
 
        init_controller(m66592);
@@ -1653,7 +1664,12 @@ static int __init m66592_probe(struct platform_device *pdev)
        dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
        return 0;
 
+clean_up3:
+#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
+       clk_disable(m66592->clk);
+       clk_put(m66592->clk);
 clean_up2:
+#endif
        free_irq(irq, m66592);
 clean_up:
        if (m66592) {
index f118f00..286ce07 100644 (file)
 #ifndef __M66592_UDC_H__
 #define __M66592_UDC_H__
 
+#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
+#include <linux/clk.h>
+#endif
+
 #define M66592_SYSCFG          0x00
 #define M66592_XTAL            0xC000  /* b15-14: Crystal selection */
 #define   M66592_XTAL48                 0x8000         /* 48MHz */
@@ -476,6 +480,9 @@ struct m66592_ep {
 struct m66592 {
        spinlock_t              lock;
        void __iomem            *reg;
+#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
+       struct clk *clk;
+#endif
 
        struct usb_gadget               gadget;
        struct usb_gadget_driver        *driver;
@@ -604,26 +611,6 @@ static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat,
 #define m66592_bset(m66592, val, offset)       \
                        m66592_mdfy(m66592, val, 0, offset)
 
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592)
-#include <asm/io.h>
-#define MSTPCR2                0xA4150038      /* for SH7722 */
-#define MSTPCR2_USB    0x00000800
-
-static inline void usbf_start_clock(void)
-{
-       ctrl_outl(ctrl_inl(MSTPCR2) & ~MSTPCR2_USB, MSTPCR2);
-}
-
-static inline void usbf_stop_clock(void)
-{
-       ctrl_outl(ctrl_inl(MSTPCR2) | MSTPCR2_USB, MSTPCR2);
-}
-
-#else
-#define usbf_start_clock(x)
-#define usbf_stop_clock(x)
-#endif /* if defined(CONFIG_SUPERH_BUILT_IN_M66592) */
-
 #endif /* ifndef __M66592_UDC_H__ */
 
 
index 2376f24..c21f14e 100644 (file)
@@ -114,6 +114,9 @@ static int r8a66597_clock_enable(struct r8a66597 *r8a66597)
        int i = 0;
 
 #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
+#if defined(CONFIG_HAVE_CLK)
+       clk_enable(r8a66597->clk);
+#endif
        do {
                r8a66597_write(r8a66597, SCKE, SYSCFG0);
                tmp = r8a66597_read(r8a66597, SYSCFG0);
@@ -154,7 +157,11 @@ static void r8a66597_clock_disable(struct r8a66597 *r8a66597)
 {
        r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
        udelay(1);
-#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
+#if defined(CONFIG_HAVE_CLK)
+       clk_disable(r8a66597->clk);
+#endif
+#else
        r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
        r8a66597_bclr(r8a66597, XCKE, SYSCFG0);
        r8a66597_bclr(r8a66597, USBE, SYSCFG0);
@@ -2261,6 +2268,9 @@ static int __init_or_module r8a66597_remove(struct platform_device *pdev)
        del_timer_sync(&r8a66597->rh_timer);
        usb_remove_hcd(hcd);
        iounmap((void *)r8a66597->reg);
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
+       clk_put(r8a66597->clk);
+#endif
        usb_put_hcd(hcd);
        return 0;
 }
@@ -2268,6 +2278,9 @@ static int __init_or_module r8a66597_remove(struct platform_device *pdev)
 #define resource_len(r) (((r)->end - (r)->start) + 1)
 static int __init r8a66597_probe(struct platform_device *pdev)
 {
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
+       char clk_name[8];
+#endif
        struct resource *res = NULL, *ires;
        int irq = -1;
        void __iomem *reg = NULL;
@@ -2320,6 +2333,16 @@ static int __init r8a66597_probe(struct platform_device *pdev)
        memset(r8a66597, 0, sizeof(struct r8a66597));
        dev_set_drvdata(&pdev->dev, r8a66597);
 
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
+       snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id);
+       r8a66597->clk = clk_get(&pdev->dev, clk_name);
+       if (IS_ERR(r8a66597->clk)) {
+               dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+               ret = PTR_ERR(r8a66597->clk);
+               goto clean_up2;
+       }
+#endif
+
        spin_lock_init(&r8a66597->lock);
        init_timer(&r8a66597->rh_timer);
        r8a66597->rh_timer.function = r8a66597_timer;
@@ -2365,11 +2388,18 @@ static int __init r8a66597_probe(struct platform_device *pdev)
        ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | irq_trigger);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to add hcd\n");
-               goto clean_up;
+               goto clean_up3;
        }
 
        return 0;
 
+clean_up3:
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
+       clk_put(r8a66597->clk);
+clean_up2:
+#endif
+       usb_put_hcd(hcd);
+
 clean_up:
        if (reg)
                iounmap(reg);
index 84ee014..ecacde4 100644 (file)
 #ifndef __R8A66597_H__
 #define __R8A66597_H__
 
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
+#include <linux/clk.h>
+#endif
+
 #define SYSCFG0                0x00
 #define SYSCFG1                0x02
 #define SYSSTS0                0x04
@@ -481,7 +485,9 @@ struct r8a66597_root_hub {
 struct r8a66597 {
        spinlock_t lock;
        unsigned long reg;
-
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
+       struct clk *clk;
+#endif
        struct r8a66597_device          device0;
        struct r8a66597_root_hub        root_hub[R8A66597_MAX_ROOT_HUB];
        struct list_head                pipe_queue[R8A66597_MAX_NUM_PIPE];
index 3f3ce13..d0c8219 100644 (file)
@@ -1889,10 +1889,11 @@ config FB_W100
 config FB_SH_MOBILE_LCDC
        tristate "SuperH Mobile LCDC framebuffer support"
        depends on FB && SUPERH
-       select FB_CFB_FILLRECT
-       select FB_CFB_COPYAREA
-       select FB_CFB_IMAGEBLIT
-       default m
+       select FB_SYS_FILLRECT
+       select FB_SYS_COPYAREA
+       select FB_SYS_IMAGEBLIT
+       select FB_SYS_FOPS
+       select FB_DEFERRED_IO
        ---help---
          Frame buffer driver for the on-chip SH-Mobile LCD controller.
 
@@ -2021,17 +2022,19 @@ config FB_COBALT
        depends on FB && MIPS_COBALT
 
 config FB_SH7760
-       bool "SH7760/SH7763 LCDC support"
-       depends on FB && (CPU_SUBTYPE_SH7760 || CPU_SUBTYPE_SH7763)
-       select FB_CFB_FILLRECT
-       select FB_CFB_COPYAREA
-       select FB_CFB_IMAGEBLIT
-       help
-         Support for the SH7760/SH7763 integrated (D)STN/TFT LCD Controller.
-         Supports display resolutions up to 1024x1024 pixel, grayscale and
-         color operation, with depths ranging from 1 bpp to 8 bpp monochrome
-         and 8, 15 or 16 bpp color; 90 degrees clockwise display rotation for
-         panels <= 320 pixel horizontal resolution.
+       bool "SH7760/SH7763/SH7720/SH7721 LCDC support"
+       depends on FB && (CPU_SUBTYPE_SH7760 || CPU_SUBTYPE_SH7763 \
+               || CPU_SUBTYPE_SH7720 || CPU_SUBTYPE_SH7721)
+       select FB_CFB_FILLRECT
+       select FB_CFB_COPYAREA
+       select FB_CFB_IMAGEBLIT
+       ---help---
+         Support for the SH7760/SH7763/SH7720/SH7721 integrated
+         (D)STN/TFT LCD Controller.
+         Supports display resolutions up to 1024x1024 pixel, grayscale and
+         color operation, with depths ranging from 1 bpp to 8 bpp monochrome
+         and 8, 15 or 16 bpp color; 90 degrees clockwise display rotation for
+         panels <= 320 pixel horizontal resolution.
 
 config FB_VIRTUAL
        tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
index 4835bdc..0820265 100644 (file)
 #include <linux/rmap.h>
 #include <linux/pagemap.h>
 
+struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs)
+{
+       void *screen_base = (void __force *) info->screen_base;
+       struct page *page;
+
+       if (is_vmalloc_addr(screen_base + offs))
+               page = vmalloc_to_page(screen_base + offs);
+       else
+               page = pfn_to_page((info->fix.smem_start + offs) >> PAGE_SHIFT);
+
+       return page;
+}
+
 /* this is to find and return the vmalloc-ed fb pages */
 static int fb_deferred_io_fault(struct vm_area_struct *vma,
                                struct vm_fault *vmf)
@@ -31,14 +44,12 @@ static int fb_deferred_io_fault(struct vm_area_struct *vma,
        unsigned long offset;
        struct page *page;
        struct fb_info *info = vma->vm_private_data;
-       /* info->screen_base is virtual memory */
-       void *screen_base = (void __force *) info->screen_base;
 
        offset = vmf->pgoff << PAGE_SHIFT;
        if (offset >= info->fix.smem_len)
                return VM_FAULT_SIGBUS;
 
-       page = vmalloc_to_page(screen_base + offset);
+       page = fb_deferred_io_page(info, offset);
        if (!page)
                return VM_FAULT_SIGBUS;
 
@@ -60,6 +71,10 @@ int fb_deferred_io_fsync(struct file *file, struct dentry *dentry, int datasync)
 {
        struct fb_info *info = file->private_data;
 
+       /* Skip if deferred io is complied-in but disabled on this fbdev */
+       if (!info->fbdefio)
+               return 0;
+
        /* Kill off the delayed work */
        cancel_rearming_delayed_work(&info->deferred_work);
 
@@ -184,7 +199,6 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_open);
 
 void fb_deferred_io_cleanup(struct fb_info *info)
 {
-       void *screen_base = (void __force *) info->screen_base;
        struct fb_deferred_io *fbdefio = info->fbdefio;
        struct page *page;
        int i;
@@ -195,9 +209,12 @@ void fb_deferred_io_cleanup(struct fb_info *info)
 
        /* clear out the mapping that we setup */
        for (i = 0 ; i < info->fix.smem_len; i += PAGE_SIZE) {
-               page = vmalloc_to_page(screen_base + i);
+               page = fb_deferred_io_page(info, i);
                page->mapping = NULL;
        }
+
+       info->fbops->fb_mmap = NULL;
+       mutex_destroy(&fbdefio->lock);
 }
 EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
 
index 8d0212d..653bdfe 100644 (file)
@@ -13,6 +13,8 @@
  *
  * Thanks to Siegfried Schaefer <s.schaefer at schaefer-edv.de>
  *     for his original source and testing!
+ *
+ * sh7760_setcolreg get from drivers/video/sh_mobile_lcdcfb.c
  */
 
 #include <linux/completion.h>
@@ -53,29 +55,6 @@ static irqreturn_t sh7760fb_irq(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-static void sh7760fb_wait_vsync(struct fb_info *info)
-{
-       struct sh7760fb_par *par = info->par;
-
-       if (par->pd->novsync)
-               return;
-
-       iowrite16(ioread16(par->base + LDINTR) & ~VINT_CHECK,
-                 par->base + LDINTR);
-
-       if (par->irq < 0) {
-               /* poll for vert. retrace: status bit is sticky */
-               while (!(ioread16(par->base + LDINTR) & VINT_CHECK))
-                       cpu_relax();
-       } else {
-               /* a "wait_for_irq_event(par->irq)" would be extremely nice */
-               init_completion(&par->vsync);
-               enable_irq(par->irq);
-               wait_for_completion(&par->vsync);
-               disable_irq_nosync(par->irq);
-       }
-}
-
 /* wait_for_lps - wait until power supply has reached a certain state. */
 static int wait_for_lps(struct sh7760fb_par *par, int val)
 {
@@ -117,55 +96,28 @@ static int sh7760fb_blank(int blank, struct fb_info *info)
        return wait_for_lps(par, lps);
 }
 
-/* set color registers */
-static int sh7760fb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
+static int sh7760_setcolreg (u_int regno,
+       u_int red, u_int green, u_int blue,
+       u_int transp, struct fb_info *info)
 {
-       struct sh7760fb_par *par = info->par;
-       u32 s = cmap->start;
-       u32 l = cmap->len;
-       u16 *r = cmap->red;
-       u16 *g = cmap->green;
-       u16 *b = cmap->blue;
-       u32 col, tmo;
-       int ret;
+       u32 *palette = info->pseudo_palette;
 
-       ret = 0;
+       if (regno >= 16)
+               return -EINVAL;
 
-       sh7760fb_wait_vsync(info);
+       /* only FB_VISUAL_TRUECOLOR supported */
 
-       /* request palette access */
-       iowrite16(LDPALCR_PALEN, par->base + LDPALCR);
+       red >>= 16 - info->var.red.length;
+       green >>= 16 - info->var.green.length;
+       blue >>= 16 - info->var.blue.length;
+       transp >>= 16 - info->var.transp.length;
 
-       /* poll for access grant */
-       tmo = 100;
-       while (!(ioread16(par->base + LDPALCR) & LDPALCR_PALS) && (--tmo))
-               cpu_relax();
+       palette[regno] = (red << info->var.red.offset) |
+               (green << info->var.green.offset) |
+               (blue << info->var.blue.offset) |
+               (transp << info->var.transp.offset);
 
-       if (!tmo) {
-               ret = 1;
-               dev_dbg(info->dev, "no palette access!\n");
-               goto out;
-       }
-
-       while (l && (s < 256)) {
-               col = ((*r) & 0xff) << 16;
-               col |= ((*g) & 0xff) << 8;
-               col |= ((*b) & 0xff);
-               col &= SH7760FB_PALETTE_MASK;
-               iowrite32(col, par->base + LDPR(s));
-
-               if (s < 16)
-                       ((u32 *) (info->pseudo_palette))[s] = s;
-
-               s++;
-               l--;
-               r++;
-               g++;
-               b++;
-       }
-out:
-       iowrite16(0, par->base + LDPALCR);
-       return ret;
+       return 0;
 }
 
 static void encode_fix(struct fb_fix_screeninfo *fix, struct fb_info *info,
@@ -406,7 +358,7 @@ static struct fb_ops sh7760fb_ops = {
        .owner = THIS_MODULE,
        .fb_blank = sh7760fb_blank,
        .fb_check_var = sh7760fb_check_var,
-       .fb_setcmap = sh7760fb_setcmap,
+       .fb_setcolreg = sh7760_setcolreg,
        .fb_set_par = sh7760fb_set_par,
        .fb_fillrect = cfb_fillrect,
        .fb_copyarea = cfb_copyarea,
index efff672..0e2b8fd 100644 (file)
@@ -16,7 +16,9 @@
 #include <linux/clk.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
 #include <video/sh_mobile_lcdc.h>
+#include <asm/atomic.h>
 
 #define PALETTE_NR 16
 
@@ -30,11 +32,15 @@ struct sh_mobile_lcdc_chan {
        u32 pseudo_palette[PALETTE_NR];
        struct fb_info info;
        dma_addr_t dma_handle;
+       struct fb_deferred_io defio;
 };
 
 struct sh_mobile_lcdc_priv {
        void __iomem *base;
+       int irq;
 #ifdef CONFIG_HAVE_CLK
+       atomic_t clk_usecnt;
+       struct clk *dot_clk;
        struct clk *clk;
 #endif
        unsigned long lddckr;
@@ -56,7 +62,7 @@ struct sh_mobile_lcdc_priv {
 
 /* per-channel registers */
 enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
-       LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR };
+       LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR };
 
 static unsigned long lcdc_offs_mainlcd[] = {
        [LDDCKPAT1R] = 0x400,
@@ -66,6 +72,7 @@ static unsigned long lcdc_offs_mainlcd[] = {
        [LDMT3R] = 0x420,
        [LDDFR] = 0x424,
        [LDSM1R] = 0x428,
+       [LDSM2R] = 0x42c,
        [LDSA1R] = 0x430,
        [LDMLSR] = 0x438,
        [LDHCNR] = 0x448,
@@ -83,6 +90,7 @@ static unsigned long lcdc_offs_sublcd[] = {
        [LDMT3R] = 0x608,
        [LDDFR] = 0x60c,
        [LDSM1R] = 0x610,
+       [LDSM2R] = 0x614,
        [LDSA1R] = 0x618,
        [LDMLSR] = 0x620,
        [LDHCNR] = 0x624,
@@ -96,6 +104,8 @@ static unsigned long lcdc_offs_sublcd[] = {
 #define LCDC_RESET     0x00000100
 #define DISPLAY_BEU    0x00000008
 #define LCDC_ENABLE    0x00000001
+#define LDINTR_FE      0x00000400
+#define LDINTR_FS      0x00000004
 
 static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
                            int reg_nr, unsigned long data)
@@ -170,6 +180,65 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
        lcdc_sys_read_data,
 };
 
+#ifdef CONFIG_HAVE_CLK
+static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
+{
+       if (atomic_inc_and_test(&priv->clk_usecnt)) {
+               clk_enable(priv->clk);
+               if (priv->dot_clk)
+                       clk_enable(priv->dot_clk);
+       }
+}
+
+static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv)
+{
+       if (atomic_sub_return(1, &priv->clk_usecnt) == -1) {
+               if (priv->dot_clk)
+                       clk_disable(priv->dot_clk);
+               clk_disable(priv->clk);
+       }
+}
+#else
+static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) {}
+static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) {}
+#endif
+
+static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
+                                      struct list_head *pagelist)
+{
+       struct sh_mobile_lcdc_chan *ch = info->par;
+
+       /* enable clocks before accessing hardware */
+       sh_mobile_lcdc_clk_on(ch->lcdc);
+
+       /* trigger panel update */
+       lcdc_write_chan(ch, LDSM2R, 1);
+}
+
+static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)
+{
+       struct fb_deferred_io *fbdefio = info->fbdefio;
+
+       if (fbdefio)
+               schedule_delayed_work(&info->deferred_work, fbdefio->delay);
+}
+
+static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
+{
+       struct sh_mobile_lcdc_priv *priv = data;
+       unsigned long tmp;
+
+       /* acknowledge interrupt */
+       tmp = lcdc_read(priv, _LDINTR);
+       tmp &= 0xffffff00; /* mask in high 24 bits */
+       tmp |= 0x000000ff ^ LDINTR_FS; /* status in low 8 */
+       lcdc_write(priv, _LDINTR, tmp);
+
+       /* disable clocks */
+       sh_mobile_lcdc_clk_off(priv);
+       return IRQ_HANDLED;
+}
+
 static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
                                      int start)
 {
@@ -207,6 +276,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
        int k, m;
        int ret = 0;
 
+       /* enable clocks before accessing the hardware */
+       for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
+               if (priv->ch[k].enabled)
+                       sh_mobile_lcdc_clk_on(priv);
+
        /* reset */
        lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
        lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);
@@ -249,7 +323,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
        lcdc_write(priv, _LDDCKSTPR, 0);
        lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);
 
-       /* interrupts are disabled */
+       /* interrupts are disabled to begin with */
        lcdc_write(priv, _LDINTR, 0);
 
        for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
@@ -310,9 +384,6 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
                        return ret;
        }
 
-       /* --- display_lcdc_data() --- */
-       lcdc_write(priv, _LDINTR, 0x00000f00);
-
        /* word and long word swap */
        lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6);
 
@@ -334,8 +405,24 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
                /* set line size */
                lcdc_write_chan(ch, LDMLSR, ch->info.fix.line_length);
 
-               /* continuous read mode */
-               lcdc_write_chan(ch, LDSM1R, 0);
+               /* setup deferred io if SYS bus */
+               tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
+               if (ch->ldmt1r_value & (1 << 12) && tmp) {
+                       ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
+                       ch->defio.delay = msecs_to_jiffies(tmp);
+                       ch->info.fbdefio = &ch->defio;
+                       fb_deferred_io_init(&ch->info);
+
+                       /* one-shot mode */
+                       lcdc_write_chan(ch, LDSM1R, 1);
+
+                       /* enable "Frame End Interrupt Enable" bit */
+                       lcdc_write(priv, _LDINTR, LDINTR_FE);
+
+               } else {
+                       /* continuous read mode */
+                       lcdc_write_chan(ch, LDSM1R, 0);
+               }
        }
 
        /* display output */
@@ -359,6 +446,7 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
 {
        struct sh_mobile_lcdc_chan *ch;
        struct sh_mobile_lcdc_board_cfg *board_cfg;
+       unsigned long tmp;
        int k;
 
        /* tell the board code to disable the panel */
@@ -367,10 +455,22 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
                board_cfg = &ch->cfg.board_cfg;
                if (board_cfg->display_off)
                        board_cfg->display_off(board_cfg->board_data);
+
+               /* cleanup deferred io if SYS bus */
+               tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
+               if (ch->ldmt1r_value & (1 << 12) && tmp) {
+                       fb_deferred_io_cleanup(&ch->info);
+                       ch->info.fbdefio = NULL;
+               }
        }
 
        /* stop the lcdc */
        sh_mobile_lcdc_start_stop(priv, 0);
+
+       /* stop clocks */
+       for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
+               if (priv->ch[k].enabled)
+                       sh_mobile_lcdc_clk_off(priv);
 }
 
 static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
@@ -413,9 +513,13 @@ static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
        return -EINVAL;
 }
 
-static int sh_mobile_lcdc_setup_clocks(struct device *dev, int clock_source,
+static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
+                                      int clock_source,
                                       struct sh_mobile_lcdc_priv *priv)
 {
+#ifdef CONFIG_HAVE_CLK
+       char clk_name[8];
+#endif
        char *str;
        int icksel;
 
@@ -430,14 +534,21 @@ static int sh_mobile_lcdc_setup_clocks(struct device *dev, int clock_source,
        priv->lddckr = icksel << 16;
 
 #ifdef CONFIG_HAVE_CLK
+       atomic_set(&priv->clk_usecnt, -1);
+       snprintf(clk_name, sizeof(clk_name), "lcdc%d", pdev->id);
+       priv->clk = clk_get(&pdev->dev, clk_name);
+       if (IS_ERR(priv->clk)) {
+               dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+               return PTR_ERR(priv->clk);
+       }
+       
        if (str) {
-               priv->clk = clk_get(dev, str);
-               if (IS_ERR(priv->clk)) {
-                       dev_err(dev, "cannot get clock %s\n", str);
-                       return PTR_ERR(priv->clk);
+               priv->dot_clk = clk_get(&pdev->dev, str);
+               if (IS_ERR(priv->dot_clk)) {
+                       dev_err(&pdev->dev, "cannot get dot clock %s\n", str);
+                       clk_put(priv->clk);
+                       return PTR_ERR(priv->dot_clk);
                }
-
-               clk_enable(priv->clk);
        }
 #endif
 
@@ -475,11 +586,34 @@ static struct fb_fix_screeninfo sh_mobile_lcdc_fix  = {
        .accel =        FB_ACCEL_NONE,
 };
 
+static void sh_mobile_lcdc_fillrect(struct fb_info *info,
+                                   const struct fb_fillrect *rect)
+{
+       sys_fillrect(info, rect);
+       sh_mobile_lcdc_deferred_io_touch(info);
+}
+
+static void sh_mobile_lcdc_copyarea(struct fb_info *info,
+                                   const struct fb_copyarea *area)
+{
+       sys_copyarea(info, area);
+       sh_mobile_lcdc_deferred_io_touch(info);
+}
+
+static void sh_mobile_lcdc_imageblit(struct fb_info *info,
+                                    const struct fb_image *image)
+{
+       sys_imageblit(info, image);
+       sh_mobile_lcdc_deferred_io_touch(info);
+}
+
 static struct fb_ops sh_mobile_lcdc_ops = {
        .fb_setcolreg   = sh_mobile_lcdc_setcolreg,
-       .fb_fillrect    = cfb_fillrect,
-       .fb_copyarea    = cfb_copyarea,
-       .fb_imageblit   = cfb_imageblit,
+       .fb_read        = fb_sys_read,
+       .fb_write       = fb_sys_write,
+       .fb_fillrect    = sh_mobile_lcdc_fillrect,
+       .fb_copyarea    = sh_mobile_lcdc_copyarea,
+       .fb_imageblit   = sh_mobile_lcdc_imageblit,
 };
 
 static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
@@ -540,8 +674,9 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (res == NULL) {
-               dev_err(&pdev->dev, "cannot find IO resource\n");
+       i = platform_get_irq(pdev, 0);
+       if (!res || i < 0) {
+               dev_err(&pdev->dev, "cannot get platform resources\n");
                error = -ENOENT;
                goto err0;
        }
@@ -553,6 +688,14 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
                goto err0;
        }
 
+       error = request_irq(i, sh_mobile_lcdc_irq, IRQF_DISABLED,
+                           pdev->dev.bus_id, priv);
+       if (error) {
+               dev_err(&pdev->dev, "unable to request irq\n");
+               goto err1;
+       }
+
+       priv->irq = i;
        platform_set_drvdata(pdev, priv);
        pdata = pdev->dev.platform_data;
 
@@ -587,8 +730,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
                goto err1;
        }
 
-       error = sh_mobile_lcdc_setup_clocks(&pdev->dev,
-                                           pdata->clock_source, priv);
+       error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv);
        if (error) {
                dev_err(&pdev->dev, "unable to setup clocks\n");
                goto err1;
@@ -637,6 +779,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
                info->fix.smem_start = priv->ch[i].dma_handle;
                info->screen_base = buf;
                info->device = &pdev->dev;
+               info->par = &priv->ch[i];
        }
 
        if (error)
@@ -664,6 +807,10 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
                         (int) priv->ch[i].cfg.lcd_cfg.xres,
                         (int) priv->ch[i].cfg.lcd_cfg.yres,
                         priv->ch[i].cfg.bpp);
+
+               /* deferred io mode: disable clock to save power */
+               if (info->fbdefio)
+                       sh_mobile_lcdc_clk_off(priv);
        }
 
        return 0;
@@ -697,15 +844,16 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
        }
 
 #ifdef CONFIG_HAVE_CLK
-       if (priv->clk) {
-               clk_disable(priv->clk);
-               clk_put(priv->clk);
-       }
+       if (priv->dot_clk)
+               clk_put(priv->dot_clk);
+       clk_put(priv->clk);
 #endif
 
        if (priv->base)
                iounmap(priv->base);
 
+       if (priv->irq)
+               free_irq(priv->irq, priv);
        kfree(priv);
        return 0;
 }
index 2a983d4..14d9442 100644 (file)
@@ -120,7 +120,7 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
        switch (access) {
        case V9FS_ACCESS_SINGLE:
        case V9FS_ACCESS_USER:
-               uid = current->fsuid;
+               uid = current_fsuid();
                any = 0;
                break;
 
index 2dfcf54..81f8bbf 100644 (file)
@@ -215,8 +215,8 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode)
        inode = new_inode(sb);
        if (inode) {
                inode->i_mode = mode;
-               inode->i_uid = current->fsuid;
-               inode->i_gid = current->fsgid;
+               inode->i_uid = current_fsuid();
+               inode->i_gid = current_fsgid();
                inode->i_blocks = 0;
                inode->i_rdev = 0;
                inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
index d6cb1a0..93212e4 100644 (file)
@@ -113,8 +113,8 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
        struct v9fs_session_info *v9ses = NULL;
        struct p9_wstat *st = NULL;
        int mode = S_IRWXUGO | S_ISVTX;
-       uid_t uid = current->fsuid;
-       gid_t gid = current->fsgid;
+       uid_t uid = current_fsuid();
+       gid_t gid = current_fsgid();
        struct p9_fid *fid;
        int retval = 0;
 
index a13b334..415d9c6 100644 (file)
@@ -293,8 +293,8 @@ affs_new_inode(struct inode *dir)
        mark_buffer_dirty_inode(bh, inode);
        affs_brelse(bh);
 
-       inode->i_uid     = current->fsuid;
-       inode->i_gid     = current->fsgid;
+       inode->i_uid     = current_fsuid();
+       inode->i_gid     = current_fsgid();
        inode->i_ino     = block;
        inode->i_nlink   = 1;
        inode->i_mtime   = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
index 8989c93..a19d64b 100644 (file)
@@ -163,8 +163,8 @@ parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s
 
        /* Fill in defaults */
 
-       *uid        = current->uid;
-       *gid        = current->gid;
+       *uid        = current_uid();
+       *gid        = current_gid();
        *reserved   = 2;
        *root       = -1;
        *blocksize  = -1;
index 3662dd4..c16d9be 100644 (file)
@@ -154,8 +154,8 @@ static struct inode *anon_inode_mkinode(void)
         */
        inode->i_state = I_DIRTY;
        inode->i_mode = S_IRUSR | S_IWUSR;
-       inode->i_uid = current->fsuid;
-       inode->i_gid = current->fsgid;
+       inode->i_uid = current_fsuid();
+       inode->i_gid = current_fsgid();
        inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
        return inode;
 }
index 7a83819..f436019 100644 (file)
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -29,13 +29,13 @@ int inode_change_ok(struct inode *inode, struct iattr *attr)
 
        /* Make sure a caller can chown. */
        if ((ia_valid & ATTR_UID) &&
-           (current->fsuid != inode->i_uid ||
+           (current_fsuid() != inode->i_uid ||
             attr->ia_uid != inode->i_uid) && !capable(CAP_CHOWN))
                goto error;
 
        /* Make sure caller can chgrp. */
        if ((ia_valid & ATTR_GID) &&
-           (current->fsuid != inode->i_uid ||
+           (current_fsuid() != inode->i_uid ||
            (!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid)) &&
            !capable(CAP_CHOWN))
                goto error;
index b70eea1..c773680 100644 (file)
@@ -76,8 +76,8 @@ static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid,
        substring_t args[MAX_OPT_ARGS];
        int option;
 
-       *uid = current->uid;
-       *gid = current->gid;
+       *uid = current_uid();
+       *gid = current_gid();
        *pgrp = task_pgrp_nr(current);
 
        *minproto = *maxproto = AUTOFS_PROTO_VERSION;
index 33bf8cb..63b7c7a 100644 (file)
@@ -308,7 +308,8 @@ static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid)
                        goto out;
                }
 
-               filp = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY);
+               filp = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY,
+                                  current_cred());
                if (IS_ERR(filp)) {
                        err = PTR_ERR(filp);
                        goto out;
index c7e65bb..7b19802 100644 (file)
@@ -235,8 +235,8 @@ static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid,
        substring_t args[MAX_OPT_ARGS];
        int option;
 
-       *uid = current->uid;
-       *gid = current->gid;
+       *uid = current_uid();
+       *gid = current_gid();
        *pgrp = task_pgrp_nr(current);
 
        *minproto = AUTOFS_MIN_PROTO_VERSION;
index 4b67c2a..e02cc8a 100644 (file)
@@ -391,8 +391,8 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry,
                memcpy(&wq->name, &qstr, sizeof(struct qstr));
                wq->dev = autofs4_get_dev(sbi);
                wq->ino = autofs4_get_ino(sbi);
-               wq->uid = current->uid;
-               wq->gid = current->gid;
+               wq->uid = current_uid();
+               wq->gid = current_gid();
                wq->pid = current->pid;
                wq->tgid = current->tgid;
                wq->status = -EINTR; /* Status return if interrupted */
index daae463..4dd1b62 100644 (file)
@@ -106,8 +106,8 @@ static int bfs_create(struct inode *dir, struct dentry *dentry, int mode,
        }
        set_bit(ino, info->si_imap);
        info->si_freei--;
-       inode->i_uid = current->fsuid;
-       inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+       inode->i_uid = current_fsuid();
+       inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current_fsgid();
        inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
        inode->i_blocks = 0;
        inode->i_op = &bfs_file_inops;
index 204cfd1..f1f3f41 100644 (file)
@@ -320,7 +320,7 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
        current->mm->free_area_cache = current->mm->mmap_base;
        current->mm->cached_hole_size = 0;
 
-       compute_creds(bprm);
+       install_exec_creds(bprm);
        current->flags &= ~PF_FORKNOEXEC;
 #ifdef __sparc__
        if (N_MAGIC(ex) == NMAGIC) {
index 8fcfa39..f458c12 100644 (file)
@@ -157,7 +157,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
        int items;
        elf_addr_t *elf_info;
        int ei_index = 0;
-       struct task_struct *tsk = current;
+       const struct cred *cred = current_cred();
        struct vm_area_struct *vma;
 
        /*
@@ -223,10 +223,10 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
        NEW_AUX_ENT(AT_BASE, interp_load_addr);
        NEW_AUX_ENT(AT_FLAGS, 0);
        NEW_AUX_ENT(AT_ENTRY, exec->e_entry);
-       NEW_AUX_ENT(AT_UID, tsk->uid);
-       NEW_AUX_ENT(AT_EUID, tsk->euid);
-       NEW_AUX_ENT(AT_GID, tsk->gid);
-       NEW_AUX_ENT(AT_EGID, tsk->egid);
+       NEW_AUX_ENT(AT_UID, cred->uid);
+       NEW_AUX_ENT(AT_EUID, cred->euid);
+       NEW_AUX_ENT(AT_GID, cred->gid);
+       NEW_AUX_ENT(AT_EGID, cred->egid);
        NEW_AUX_ENT(AT_SECURE, security_bprm_secureexec(bprm));
        NEW_AUX_ENT(AT_EXECFN, bprm->exec);
        if (k_platform) {
@@ -956,7 +956,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
        }
 #endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */
 
-       compute_creds(bprm);
+       install_exec_creds(bprm);
        current->flags &= ~PF_FORKNOEXEC;
        retval = create_elf_tables(bprm, &loc->elf_ex,
                          load_addr, interp_load_addr);
@@ -1361,6 +1361,7 @@ static void fill_prstatus(struct elf_prstatus *prstatus,
 static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
                       struct mm_struct *mm)
 {
+       const struct cred *cred;
        unsigned int i, len;
        
        /* first copy the parameters from user space */
@@ -1388,8 +1389,11 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
        psinfo->pr_zomb = psinfo->pr_sname == 'Z';
        psinfo->pr_nice = task_nice(p);
        psinfo->pr_flag = p->flags;
-       SET_UID(psinfo->pr_uid, p->uid);
-       SET_GID(psinfo->pr_gid, p->gid);
+       rcu_read_lock();
+       cred = __task_cred(p);
+       SET_UID(psinfo->pr_uid, cred->uid);
+       SET_GID(psinfo->pr_gid, cred->gid);
+       rcu_read_unlock();
        strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname));
        
        return 0;
index 5b5424c..aa5b432 100644 (file)
@@ -404,7 +404,7 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm,
        current->mm->start_stack = current->mm->start_brk + stack_size;
 #endif
 
-       compute_creds(bprm);
+       install_exec_creds(bprm);
        current->flags &= ~PF_FORKNOEXEC;
        if (create_elf_fdpic_tables(bprm, current->mm,
                                    &exec_params, &interp_params) < 0)
@@ -475,6 +475,7 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
                                   struct elf_fdpic_params *exec_params,
                                   struct elf_fdpic_params *interp_params)
 {
+       const struct cred *cred = current_cred();
        unsigned long sp, csp, nitems;
        elf_caddr_t __user *argv, *envp;
        size_t platform_len = 0, len;
@@ -623,10 +624,10 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
        NEW_AUX_ENT(AT_BASE,    interp_params->elfhdr_addr);
        NEW_AUX_ENT(AT_FLAGS,   0);
        NEW_AUX_ENT(AT_ENTRY,   exec_params->entry_addr);
-       NEW_AUX_ENT(AT_UID,     (elf_addr_t) current->uid);
-       NEW_AUX_ENT(AT_EUID,    (elf_addr_t) current->euid);
-       NEW_AUX_ENT(AT_GID,     (elf_addr_t) current->gid);
-       NEW_AUX_ENT(AT_EGID,    (elf_addr_t) current->egid);
+       NEW_AUX_ENT(AT_UID,     (elf_addr_t) cred->uid);
+       NEW_AUX_ENT(AT_EUID,    (elf_addr_t) cred->euid);
+       NEW_AUX_ENT(AT_GID,     (elf_addr_t) cred->gid);
+       NEW_AUX_ENT(AT_EGID,    (elf_addr_t) cred->egid);
        NEW_AUX_ENT(AT_SECURE,  security_bprm_secureexec(bprm));
        NEW_AUX_ENT(AT_EXECFN,  bprm->exec);
 
@@ -1413,6 +1414,7 @@ static void fill_prstatus(struct elf_prstatus *prstatus,
 static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
                       struct mm_struct *mm)
 {
+       const struct cred *cred;
        unsigned int i, len;
 
        /* first copy the parameters from user space */
@@ -1440,8 +1442,11 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
        psinfo->pr_zomb = psinfo->pr_sname == 'Z';
        psinfo->pr_nice = task_nice(p);
        psinfo->pr_flag = p->flags;
-       SET_UID(psinfo->pr_uid, p->uid);
-       SET_GID(psinfo->pr_gid, p->gid);
+       rcu_read_lock();
+       cred = __task_cred(p);
+       SET_UID(psinfo->pr_uid, cred->uid);
+       SET_GID(psinfo->pr_gid, cred->gid);
+       rcu_read_unlock();
        strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname));
 
        return 0;
index ccb781a..7bbd5c6 100644 (file)
@@ -880,7 +880,7 @@ static int load_flat_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                                        (libinfo.lib_list[j].loaded)?
                                                libinfo.lib_list[j].start_data:UNLOADED_LIB;
 
-       compute_creds(bprm);
+       install_exec_creds(bprm);
        current->flags &= ~PF_FORKNOEXEC;
 
        set_binfmt(&flat_format);
index 74e587a..08644a6 100644 (file)
@@ -255,7 +255,7 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs)
        kfree(hpuxhdr);
 
        set_binfmt(&som_format);
-       compute_creds(bprm);
+       install_exec_creds(bprm);
        setup_arg_pages(bprm, STACK_TOP, EXSTACK_DEFAULT);
 
        create_som_tables(bprm);
index 877c854..1e7b874 100644 (file)
@@ -19,7 +19,7 @@
 #define _CIFS_FS_SB_H
 
 #define CIFS_MOUNT_NO_PERM      1 /* do not do client vfs_perm check */
-#define CIFS_MOUNT_SET_UID      2 /* set current->euid in create etc. */
+#define CIFS_MOUNT_SET_UID      2 /* set current's euid in create etc. */
 #define CIFS_MOUNT_SERVER_INUM  4 /* inode numbers from uniqueid from server */
 #define CIFS_MOUNT_DIRECT_IO    8 /* do not write nor read through page cache */
 #define CIFS_MOUNT_NO_XATTR     0x10  /* if set - disable xattr support       */
index 6f21ecb..9d8b978 100644 (file)
@@ -39,7 +39,7 @@ extern int smb_send(struct socket *, struct smb_hdr *,
                        unsigned int /* length */ , struct sockaddr *, bool);
 extern unsigned int _GetXid(void);
 extern void _FreeXid(unsigned int);
-#define GetXid() (int)_GetXid(); cFYI(1,("CIFS VFS: in %s as Xid: %d with uid: %d",__func__, xid,current->fsuid));
+#define GetXid() (int)_GetXid(); cFYI(1,("CIFS VFS: in %s as Xid: %d with uid: %d",__func__, xid,current_fsuid()));
 #define FreeXid(curr_xid) {_FreeXid(curr_xid); cFYI(1,("CIFS VFS: leaving %s (xid = %d) rc = %d",__func__,curr_xid,(int)rc));}
 extern char *build_path_from_dentry(struct dentry *);
 extern char *build_wildcard_path_from_dentry(struct dentry *direntry);
index c7d3417..683dee4 100644 (file)
@@ -836,8 +836,8 @@ cifs_parse_mount_options(char *options, const char *devname,
        /* null target name indicates to use *SMBSERVR default called name
           if we end up sending RFC1001 session initialize */
        vol->target_rfc1001_name[0] = 0;
-       vol->linux_uid = current->uid;  /* current->euid instead? */
-       vol->linux_gid = current->gid;
+       vol->linux_uid = current_uid();  /* use current_euid() instead? */
+       vol->linux_gid = current_gid();
        vol->dir_mode = S_IRWXUGO;
        /* 2767 perms indicate mandatory locking support */
        vol->file_mode = (S_IRWXUGO | S_ISGID) & (~S_IXGRP);
index e962e75..2f02c52 100644 (file)
@@ -235,11 +235,11 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
                        };
 
                        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
-                               args.uid = (__u64) current->fsuid;
+                               args.uid = (__u64) current_fsuid();
                                if (inode->i_mode & S_ISGID)
                                        args.gid = (__u64) inode->i_gid;
                                else
-                                       args.gid = (__u64) current->fsgid;
+                                       args.gid = (__u64) current_fsgid();
                        } else {
                                args.uid = NO_CHANGE_64;
                                args.gid = NO_CHANGE_64;
@@ -271,13 +271,13 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
                                if ((oplock & CIFS_CREATE_ACTION) &&
                                    (cifs_sb->mnt_cifs_flags &
                                     CIFS_MOUNT_SET_UID)) {
-                                       newinode->i_uid = current->fsuid;
+                                       newinode->i_uid = current_fsuid();
                                        if (inode->i_mode & S_ISGID)
                                                newinode->i_gid =
                                                        inode->i_gid;
                                        else
                                                newinode->i_gid =
-                                                       current->fsgid;
+                                                       current_fsgid();
                                }
                        }
                }
@@ -375,8 +375,8 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
                        .device = device_number,
                };
                if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
-                       args.uid = (__u64) current->fsuid;
-                       args.gid = (__u64) current->fsgid;
+                       args.uid = (__u64) current_fsuid();
+                       args.gid = (__u64) current_fsgid();
                } else {
                        args.uid = NO_CHANGE_64;
                        args.gid = NO_CHANGE_64;
index ff8c68d..8b7305e 100644 (file)
@@ -1143,11 +1143,11 @@ mkdir_get_info:
                                .device = 0,
                        };
                        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
-                               args.uid = (__u64)current->fsuid;
+                               args.uid = (__u64)current_fsuid();
                                if (inode->i_mode & S_ISGID)
                                        args.gid = (__u64)inode->i_gid;
                                else
-                                       args.gid = (__u64)current->fsgid;
+                                       args.gid = (__u64)current_fsgid();
                        } else {
                                args.uid = NO_CHANGE_64;
                                args.gid = NO_CHANGE_64;
@@ -1184,13 +1184,13 @@ mkdir_get_info:
                                if (cifs_sb->mnt_cifs_flags &
                                     CIFS_MOUNT_SET_UID) {
                                        direntry->d_inode->i_uid =
-                                               current->fsuid;
+                                               current_fsuid();
                                        if (inode->i_mode & S_ISGID)
                                                direntry->d_inode->i_gid =
                                                        inode->i_gid;
                                        else
                                                direntry->d_inode->i_gid =
-                                                       current->fsgid;
+                                                       current_fsgid();
                                }
                        }
                }
index 0088a5b..f946506 100644 (file)
@@ -65,7 +65,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
        switch (command) {
                case CIFS_IOC_CHECKUMOUNT:
                        cFYI(1, ("User unmount attempted"));
-                       if (cifs_sb->mnt_uid == current->uid)
+                       if (cifs_sb->mnt_uid == current_uid())
                                rc = 0;
                        else {
                                rc = -EACCES;
index 9ee3f68..8a82d07 100644 (file)
@@ -338,13 +338,13 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
                /*  BB Add support for establishing new tCon and SMB Session  */
                /*      with userid/password pairs found on the smb session   */
                /*      for other target tcp/ip addresses               BB    */
-                               if (current->fsuid != treeCon->ses->linux_uid) {
+                               if (current_fsuid() != treeCon->ses->linux_uid) {
                                        cFYI(1, ("Multiuser mode and UID "
                                                 "did not match tcon uid"));
                                        read_lock(&cifs_tcp_ses_lock);
                                        list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) {
                                                ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list);
-                                               if (ses->linux_uid == current->fsuid) {
+                                               if (ses->linux_uid == current_fsuid()) {
                                                        if (ses->server == treeCon->ses->server) {
                                                                cFYI(1, ("found matching uid substitute right smb_uid"));
                                                                buffer->Uid = ses->Suid;
index 8a23703..a5bf577 100644 (file)
@@ -32,8 +32,8 @@ void coda_cache_enter(struct inode *inode, int mask)
        struct coda_inode_info *cii = ITOC(inode);
 
        cii->c_cached_epoch = atomic_read(&permission_epoch);
-       if (cii->c_uid != current->fsuid) {
-                cii->c_uid = current->fsuid;
+       if (cii->c_uid != current_fsuid()) {
+               cii->c_uid = current_fsuid();
                 cii->c_cached_perm = mask;
         } else
                 cii->c_cached_perm |= mask;
@@ -60,7 +60,7 @@ int coda_cache_check(struct inode *inode, int mask)
         int hit;
        
         hit = (mask & cii->c_cached_perm) == mask &&
-               cii->c_uid == current->fsuid &&
+               cii->c_uid == current_fsuid() &&
                cii->c_cached_epoch == atomic_read(&permission_epoch);
 
         return hit;
index 29137ff..466303d 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/file.h>
 #include <linux/fs.h>
 #include <linux/stat.h>
+#include <linux/cred.h>
 #include <linux/errno.h>
 #include <linux/smp_lock.h>
 #include <linux/string.h>
@@ -174,7 +175,7 @@ int coda_release(struct inode *coda_inode, struct file *coda_file)
        BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
 
        err = venus_close(coda_inode->i_sb, coda_i2f(coda_inode),
-                         coda_flags, coda_file->f_uid);
+                         coda_flags, coda_file->f_cred->fsuid);
 
        host_inode = cfi->cfi_container->f_path.dentry->d_inode;
        cii = ITOC(coda_inode);
index ce432bc..c274d94 100644 (file)
@@ -52,7 +52,7 @@ static void *alloc_upcall(int opcode, int size)
         inp->ih.opcode = opcode;
        inp->ih.pid = current->pid;
        inp->ih.pgid = task_pgrp_nr(current);
-       inp->ih.uid = current->fsuid;
+       inp->ih.uid = current_fsuid();
 
        return (void*)inp;
 }
index e5f49f5..d1ece79 100644 (file)
@@ -1393,10 +1393,20 @@ int compat_do_execve(char * filename,
        if (!bprm)
                goto out_ret;
 
+       retval = mutex_lock_interruptible(&current->cred_exec_mutex);
+       if (retval < 0)
+               goto out_free;
+
+       retval = -ENOMEM;
+       bprm->cred = prepare_exec_creds();
+       if (!bprm->cred)
+               goto out_unlock;
+       check_unsafe_exec(bprm);
+
        file = open_exec(filename);
        retval = PTR_ERR(file);
        if (IS_ERR(file))
-               goto out_kfree;
+               goto out_unlock;
 
        sched_exec();
 
@@ -1410,14 +1420,10 @@ int compat_do_execve(char * filename,
 
        bprm->argc = compat_count(argv, MAX_ARG_STRINGS);
        if ((retval = bprm->argc) < 0)
-               goto out_mm;
+               goto out;
 
        bprm->envc = compat_count(envp, MAX_ARG_STRINGS);
        if ((retval = bprm->envc) < 0)
-               goto out_mm;
-
-       retval = security_bprm_alloc(bprm);
-       if (retval)
                goto out;
 
        retval = prepare_binprm(bprm);
@@ -1438,19 +1444,16 @@ int compat_do_execve(char * filename,
                goto out;
 
        retval = search_binary_handler(bprm, regs);
-       if (retval >= 0) {
-               /* execve success */
-               security_bprm_free(bprm);
-               acct_update_integrals(current);
-               free_bprm(bprm);
-               return retval;
-       }
+       if (retval < 0)
+               goto out;
 
-out:
-       if (bprm->security)
-               security_bprm_free(bprm);
+       /* execve succeeded */
+       mutex_unlock(&current->cred_exec_mutex);
+       acct_update_integrals(current);
+       free_bprm(bprm);
+       return retval;
 
-out_mm:
+out:
        if (bprm->mm)
                mmput(bprm->mm);
 
@@ -1460,7 +1463,10 @@ out_file:
                fput(bprm->file);
        }
 
-out_kfree:
+out_unlock:
+       mutex_unlock(&current->cred_exec_mutex);
+
+out_free:
        free_bprm(bprm);
 
 out_ret:
index 4a714f6..5d61b7c 100644 (file)
@@ -222,8 +222,8 @@ int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty)
                return -ENOMEM;
 
        inode->i_ino = number+2;
-       inode->i_uid = config.setuid ? config.uid : current->fsuid;
-       inode->i_gid = config.setgid ? config.gid : current->fsgid;
+       inode->i_uid = config.setuid ? config.uid : current_fsuid();
+       inode->i_gid = config.setgid ? config.gid : current_fsgid();
        inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
        init_special_inode(inode, S_IFCHR|config.mode, device);
        inode->i_private = tty;
index 5e95261..c237ccc 100644 (file)
@@ -874,7 +874,7 @@ static inline int need_print_warning(struct dquot *dquot)
 
        switch (dquot->dq_type) {
                case USRQUOTA:
-                       return current->fsuid == dquot->dq_id;
+                       return current_fsuid() == dquot->dq_id;
                case GRPQUOTA:
                        return in_group_p(dquot->dq_id);
        }
@@ -981,7 +981,7 @@ static void send_warning(const struct dquot *dquot, const char warntype)
                MINOR(dquot->dq_sb->s_dev));
        if (ret)
                goto attr_err_out;
-       ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID, current->user->uid);
+       ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID, current_uid());
        if (ret)
                goto attr_err_out;
        genlmsg_end(skb, msg_head);
index 3504cf9..a75026d 100644 (file)
@@ -691,7 +691,8 @@ int ecryptfs_init_kthread(void);
 void ecryptfs_destroy_kthread(void);
 int ecryptfs_privileged_open(struct file **lower_file,
                             struct dentry *lower_dentry,
-                            struct vfsmount *lower_mnt);
+                            struct vfsmount *lower_mnt,
+                            const struct cred *cred);
 int ecryptfs_init_persistent_file(struct dentry *ecryptfs_dentry);
 
 #endif /* #ifndef ECRYPTFS_KERNEL_H */
index c440c6b..c6d7a4d 100644 (file)
@@ -73,7 +73,7 @@ static int ecryptfs_threadfn(void *ignored)
                                mntget(req->lower_mnt);
                                (*req->lower_file) = dentry_open(
                                        req->lower_dentry, req->lower_mnt,
-                                       (O_RDWR | O_LARGEFILE));
+                                       (O_RDWR | O_LARGEFILE), current_cred());
                                req->flags |= ECRYPTFS_REQ_PROCESSED;
                        }
                        wake_up(&req->wait);
@@ -132,7 +132,8 @@ void ecryptfs_destroy_kthread(void)
  */
 int ecryptfs_privileged_open(struct file **lower_file,
                             struct dentry *lower_dentry,
-                            struct vfsmount *lower_mnt)
+                            struct vfsmount *lower_mnt,
+                            const struct cred *cred)
 {
        struct ecryptfs_open_req *req;
        int rc = 0;
@@ -143,7 +144,7 @@ int ecryptfs_privileged_open(struct file **lower_file,
        dget(lower_dentry);
        mntget(lower_mnt);
        (*lower_file) = dentry_open(lower_dentry, lower_mnt,
-                                   (O_RDWR | O_LARGEFILE));
+                                   (O_RDWR | O_LARGEFILE), cred);
        if (!IS_ERR(*lower_file))
                goto out;
        req = kmem_cache_alloc(ecryptfs_open_req_cache, GFP_KERNEL);
@@ -184,7 +185,7 @@ int ecryptfs_privileged_open(struct file **lower_file,
                dget(lower_dentry);
                mntget(lower_mnt);
                (*lower_file) = dentry_open(lower_dentry, lower_mnt,
-                                           (O_RDONLY | O_LARGEFILE));
+                                           (O_RDONLY | O_LARGEFILE), cred);
                if (IS_ERR(*lower_file)) {
                        rc = PTR_ERR(*req->lower_file);
                        (*lower_file) = NULL;
index 64d2ba9..fd63071 100644 (file)
@@ -115,6 +115,7 @@ void __ecryptfs_printk(const char *fmt, ...)
  */
 int ecryptfs_init_persistent_file(struct dentry *ecryptfs_dentry)
 {
+       const struct cred *cred = current_cred();
        struct ecryptfs_inode_info *inode_info =
                ecryptfs_inode_to_private(ecryptfs_dentry->d_inode);
        int rc = 0;
@@ -127,7 +128,7 @@ int ecryptfs_init_persistent_file(struct dentry *ecryptfs_dentry)
 
                lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
                rc = ecryptfs_privileged_open(&inode_info->lower_file,
-                                                    lower_dentry, lower_mnt);
+                                             lower_dentry, lower_mnt, cred);
                if (rc || IS_ERR(inode_info->lower_file)) {
                        printk(KERN_ERR "Error opening lower persistent file "
                               "for lower_dentry [0x%p] and lower_mnt [0x%p]; "
index c698397..6913f72 100644 (file)
@@ -360,7 +360,8 @@ int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t euid,
        struct ecryptfs_msg_ctx *msg_ctx;
        size_t msg_size;
        struct nsproxy *nsproxy;
-       struct user_namespace *current_user_ns;
+       struct user_namespace *tsk_user_ns;
+       uid_t ctx_euid;
        int rc;
 
        if (msg->index >= ecryptfs_message_buf_len) {
@@ -384,9 +385,9 @@ int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t euid,
                mutex_unlock(&ecryptfs_daemon_hash_mux);
                goto wake_up;
        }
-       current_user_ns = nsproxy->user_ns;
-       rc = ecryptfs_find_daemon_by_euid(&daemon, msg_ctx->task->euid,
-                                         current_user_ns);
+       tsk_user_ns = __task_cred(msg_ctx->task)->user->user_ns;
+       ctx_euid = task_euid(msg_ctx->task);
+       rc = ecryptfs_find_daemon_by_euid(&daemon, ctx_euid, tsk_user_ns);
        rcu_read_unlock();
        mutex_unlock(&ecryptfs_daemon_hash_mux);
        if (rc) {
@@ -394,28 +395,28 @@ int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t euid,
                printk(KERN_WARNING "%s: User [%d] received a "
                       "message response from process [0x%p] but does "
                       "not have a registered daemon\n", __func__,
-                      msg_ctx->task->euid, pid);
+                      ctx_euid, pid);
                goto wake_up;
        }
-       if (msg_ctx->task->euid != euid) {
+       if (ctx_euid != euid) {
                rc = -EBADMSG;
                printk(KERN_WARNING "%s: Received message from user "
                       "[%d]; expected message from user [%d]\n", __func__,
-                      euid, msg_ctx->task->euid);
+                      euid, ctx_euid);
                goto unlock;
        }
-       if (current_user_ns != user_ns) {
+       if (tsk_user_ns != user_ns) {
                rc = -EBADMSG;
                printk(KERN_WARNING "%s: Received message from user_ns "
                       "[0x%p]; expected message from user_ns [0x%p]\n",
-                      __func__, user_ns, nsproxy->user_ns);
+                      __func__, user_ns, tsk_user_ns);
                goto unlock;
        }
        if (daemon->pid != pid) {
                rc = -EBADMSG;
                printk(KERN_ERR "%s: User [%d] sent a message response "
                       "from an unrecognized process [0x%p]\n",
-                      __func__, msg_ctx->task->euid, pid);
+                      __func__, ctx_euid, pid);
                goto unlock;
        }
        if (msg_ctx->state != ECRYPTFS_MSG_CTX_STATE_PENDING) {
@@ -464,14 +465,14 @@ ecryptfs_send_message_locked(char *data, int data_len, u8 msg_type,
                             struct ecryptfs_msg_ctx **msg_ctx)
 {
        struct ecryptfs_daemon *daemon;
+       uid_t euid = current_euid();
        int rc;
 
-       rc = ecryptfs_find_daemon_by_euid(&daemon, current->euid,
-                                         current->nsproxy->user_ns);
+       rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
        if (rc || !daemon) {
                rc = -ENOTCONN;
                printk(KERN_ERR "%s: User [%d] does not have a daemon "
-                      "registered\n", __func__, current->euid);
+                      "registered\n", __func__, euid);
                goto out;
        }
        mutex_lock(&ecryptfs_msg_ctx_lists_mux);
index b484792..efd95a0 100644 (file)
@@ -42,12 +42,12 @@ ecryptfs_miscdev_poll(struct file *file, poll_table *pt)
 {
        struct ecryptfs_daemon *daemon;
        unsigned int mask = 0;
+       uid_t euid = current_euid();
        int rc;
 
        mutex_lock(&ecryptfs_daemon_hash_mux);
        /* TODO: Just use file->private_data? */
-       rc = ecryptfs_find_daemon_by_euid(&daemon, current->euid,
-                                         current->nsproxy->user_ns);
+       rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
        BUG_ON(rc || !daemon);
        mutex_lock(&daemon->mux);
        mutex_unlock(&ecryptfs_daemon_hash_mux);
@@ -83,6 +83,7 @@ static int
 ecryptfs_miscdev_open(struct inode *inode, struct file *file)
 {
        struct ecryptfs_daemon *daemon = NULL;
+       uid_t euid = current_euid();
        int rc;
 
        mutex_lock(&ecryptfs_daemon_hash_mux);
@@ -93,11 +94,9 @@ ecryptfs_miscdev_open(struct inode *inode, struct file *file)
                       "count; rc = [%d]\n", __func__, rc);
                goto out_unlock_daemon_list;
        }
-       rc = ecryptfs_find_daemon_by_euid(&daemon, current->euid,
-                                         current->nsproxy->user_ns);
+       rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
        if (rc || !daemon) {
-               rc = ecryptfs_spawn_daemon(&daemon, current->euid,
-                                          current->nsproxy->user_ns,
+               rc = ecryptfs_spawn_daemon(&daemon, euid, current_user_ns(),
                                           task_pid(current));
                if (rc) {
                        printk(KERN_ERR "%s: Error attempting to spawn daemon; "
@@ -147,11 +146,11 @@ static int
 ecryptfs_miscdev_release(struct inode *inode, struct file *file)
 {
        struct ecryptfs_daemon *daemon = NULL;
+       uid_t euid = current_euid();
        int rc;
 
        mutex_lock(&ecryptfs_daemon_hash_mux);
-       rc = ecryptfs_find_daemon_by_euid(&daemon, current->euid,
-                                         current->nsproxy->user_ns);
+       rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
        BUG_ON(rc || !daemon);
        mutex_lock(&daemon->mux);
        BUG_ON(daemon->pid != task_pid(current));
@@ -246,12 +245,12 @@ ecryptfs_miscdev_read(struct file *file, char __user *buf, size_t count,
        char packet_length[3];
        size_t i;
        size_t total_length;
+       uid_t euid = current_euid();
        int rc;
 
        mutex_lock(&ecryptfs_daemon_hash_mux);
        /* TODO: Just use file->private_data? */
-       rc = ecryptfs_find_daemon_by_euid(&daemon, current->euid,
-                                         current->nsproxy->user_ns);
+       rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
        BUG_ON(rc || !daemon);
        mutex_lock(&daemon->mux);
        if (daemon->flags & ECRYPTFS_DAEMON_ZOMBIE) {
@@ -290,8 +289,8 @@ check_list:
                 * message from the queue; try again */
                goto check_list;
        }
-       BUG_ON(current->euid != daemon->euid);
-       BUG_ON(current->nsproxy->user_ns != daemon->user_ns);
+       BUG_ON(euid != daemon->euid);
+       BUG_ON(current_user_ns() != daemon->user_ns);
        BUG_ON(task_pid(current) != daemon->pid);
        msg_ctx = list_first_entry(&daemon->msg_ctx_out_queue,
                                   struct ecryptfs_msg_ctx, daemon_out_list);
@@ -414,6 +413,7 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf,
        size_t packet_size, packet_size_length, i;
        ssize_t sz = 0;
        char *data;
+       uid_t euid = current_euid();
        int rc;
 
        if (count == 0)
@@ -463,8 +463,7 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf,
                        goto out_free;
                }
                rc = ecryptfs_miscdev_response(&data[i], packet_size,
-                                              current->euid,
-                                              current->nsproxy->user_ns,
+                                              euid, current_user_ns(),
                                               task_pid(current), seq);
                if (rc)
                        printk(KERN_WARNING "%s: Failed to deliver miscdev "
index ec5df9a..1f59ea0 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -55,6 +55,7 @@
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
 #include <asm/tlb.h>
+#include "internal.h"
 
 #ifdef __alpha__
 /* for /sbin/loader handling in search_binary_handler() */
@@ -980,7 +981,7 @@ int flush_old_exec(struct linux_binprm * bprm)
        /* This is the point of no return */
        current->sas_ss_sp = current->sas_ss_size = 0;
 
-       if (current->euid == current->uid && current->egid == current->gid)
+       if (current_euid() == current_uid() && current_egid() == current_gid())
                set_dumpable(current->mm, 1);
        else
                set_dumpable(current->mm, suid_dumpable);
@@ -1007,16 +1008,17 @@ int flush_old_exec(struct linux_binprm * bprm)
         */
        current->mm->task_size = TASK_SIZE;
 
-       if (bprm->e_uid != current->euid || bprm->e_gid != current->egid) {
-               suid_keys(current);
-               set_dumpable(current->mm, suid_dumpable);
+       /* install the new credentials */
+       if (bprm->cred->uid != current_euid() ||
+           bprm->cred->gid != current_egid()) {
                current->pdeath_signal = 0;
        } else if (file_permission(bprm->file, MAY_READ) ||
-                       (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) {
-               suid_keys(current);
+                  bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP) {
                set_dumpable(current->mm, suid_dumpable);
        }
 
+       current->personality &= ~bprm->per_clear;
+
        /* An exec changes our domain. We are no longer part of the thread
           group */
 
@@ -1033,13 +1035,50 @@ out:
 
 EXPORT_SYMBOL(flush_old_exec);
 
+/*
+ * install the new credentials for this executable
+ */
+void install_exec_creds(struct linux_binprm *bprm)
+{
+       security_bprm_committing_creds(bprm);
+
+       commit_creds(bprm->cred);
+       bprm->cred = NULL;
+
+       /* cred_exec_mutex must be held at least to this point to prevent
+        * ptrace_attach() from altering our determination of the task's
+        * credentials; any time after this it may be unlocked */
+
+       security_bprm_committed_creds(bprm);
+}
+EXPORT_SYMBOL(install_exec_creds);
+
+/*
+ * determine how safe it is to execute the proposed program
+ * - the caller must hold current->cred_exec_mutex to protect against
+ *   PTRACE_ATTACH
+ */
+void check_unsafe_exec(struct linux_binprm *bprm)
+{
+       struct task_struct *p = current;
+
+       bprm->unsafe = tracehook_unsafe_exec(p);
+
+       if (atomic_read(&p->fs->count) > 1 ||
+           atomic_read(&p->files->count) > 1 ||
+           atomic_read(&p->sighand->count) > 1)
+               bprm->unsafe |= LSM_UNSAFE_SHARE;
+}
+
 /* 
  * Fill the binprm structure from the inode. 
  * Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes
+ *
+ * This may be called multiple times for binary chains (scripts for example).
  */
 int prepare_binprm(struct linux_binprm *bprm)
 {
-       int mode;
+       umode_t mode;
        struct inode * inode = bprm->file->f_path.dentry->d_inode;
        int retval;
 
@@ -1047,14 +1086,15 @@ int prepare_binprm(struct linux_binprm *bprm)
        if (bprm->file->f_op == NULL)
                return -EACCES;
 
-       bprm->e_uid = current->euid;
-       bprm->e_gid = current->egid;
+       /* clear any previous set[ug]id data from a previous binary */
+       bprm->cred->euid = current_euid();
+       bprm->cred->egid = current_egid();
 
-       if(!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {
+       if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {
                /* Set-uid? */
                if (mode & S_ISUID) {
-                       current->personality &= ~PER_CLEAR_ON_SETID;
-                       bprm->e_uid = inode->i_uid;
+                       bprm->per_clear |= PER_CLEAR_ON_SETID;
+                       bprm->cred->euid = inode->i_uid;
                }
 
                /* Set-gid? */
@@ -1064,52 +1104,23 @@ int prepare_binprm(struct linux_binprm *bprm)
                 * executable.
                 */
                if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
-                       current->personality &= ~PER_CLEAR_ON_SETID;
-                       bprm->e_gid = inode->i_gid;
+                       bprm->per_clear |= PER_CLEAR_ON_SETID;
+                       bprm->cred->egid = inode->i_gid;
                }
        }
 
        /* fill in binprm security blob */
-       retval = security_bprm_set(bprm);
+       retval = security_bprm_set_creds(bprm);
        if (retval)
                return retval;
+       bprm->cred_prepared = 1;
 
-       memset(bprm->buf,0,BINPRM_BUF_SIZE);
-       return kernel_read(bprm->file,0,bprm->buf,BINPRM_BUF_SIZE);
+       memset(bprm->buf, 0, BINPRM_BUF_SIZE);
+       return kernel_read(bprm->file, 0, bprm->buf, BINPRM_BUF_SIZE);
 }
 
 EXPORT_SYMBOL(prepare_binprm);
 
-static int unsafe_exec(struct task_struct *p)
-{
-       int unsafe = tracehook_unsafe_exec(p);
-
-       if (atomic_read(&p->fs->count) > 1 ||
-           atomic_read(&p->files->count) > 1 ||
-           atomic_read(&p->sighand->count) > 1)
-               unsafe |= LSM_UNSAFE_SHARE;
-
-       return unsafe;
-}
-
-void compute_creds(struct linux_binprm *bprm)
-{
-       int unsafe;
-
-       if (bprm->e_uid != current->uid) {
-               suid_keys(current);
-               current->pdeath_signal = 0;
-       }
-       exec_keys(current);
-
-       task_lock(current);
-       unsafe = unsafe_exec(current);
-       security_bprm_apply_creds(bprm, unsafe);
-       task_unlock(current);
-       security_bprm_post_apply_creds(bprm);
-}
-EXPORT_SYMBOL(compute_creds);
-
 /*
  * Arguments are '\0' separated strings found at the location bprm->p
  * points to; chop off the first by relocating brpm->p to right after
@@ -1270,6 +1281,8 @@ EXPORT_SYMBOL(search_binary_handler);
 void free_bprm(struct linux_binprm *bprm)
 {
        free_arg_pages(bprm);
+       if (bprm->cred)
+               abort_creds(bprm->cred);
        kfree(bprm);
 }
 
@@ -1295,10 +1308,20 @@ int do_execve(char * filename,
        if (!bprm)
                goto out_files;
 
+       retval = mutex_lock_interruptible(&current->cred_exec_mutex);
+       if (retval < 0)
+               goto out_free;
+
+       retval = -ENOMEM;
+       bprm->cred = prepare_exec_creds();
+       if (!bprm->cred)
+               goto out_unlock;
+       check_unsafe_exec(bprm);
+
        file = open_exec(filename);
        retval = PTR_ERR(file);
        if (IS_ERR(file))
-               goto out_kfree;
+               goto out_unlock;
 
        sched_exec();
 
@@ -1312,14 +1335,10 @@ int do_execve(char * filename,
 
        bprm->argc = count(argv, MAX_ARG_STRINGS);
        if ((retval = bprm->argc) < 0)
-               goto out_mm;
+               goto out;
 
        bprm->envc = count(envp, MAX_ARG_STRINGS);
        if ((retval = bprm->envc) < 0)
-               goto out_mm;
-
-       retval = security_bprm_alloc(bprm);
-       if (retval)
                goto out;
 
        retval = prepare_binprm(bprm);
@@ -1341,21 +1360,18 @@ int do_execve(char * filename,
 
        current->flags &= ~PF_KTHREAD;
        retval = search_binary_handler(bprm,regs);
-       if (retval >= 0) {
-               /* execve success */
-               security_bprm_free(bprm);
-               acct_update_integrals(current);
-               free_bprm(bprm);
-               if (displaced)
-                       put_files_struct(displaced);
-               return retval;
-       }
+       if (retval < 0)
+               goto out;
 
-out:
-       if (bprm->security)
-               security_bprm_free(bprm);
+       /* execve succeeded */
+       mutex_unlock(&current->cred_exec_mutex);
+       acct_update_integrals(current);
+       free_bprm(bprm);
+       if (displaced)
+               put_files_struct(displaced);
+       return retval;
 
-out_mm:
+out:
        if (bprm->mm)
                mmput (bprm->mm);
 
@@ -1364,7 +1380,11 @@ out_file:
                allow_write_access(bprm->file);
                fput(bprm->file);
        }
-out_kfree:
+
+out_unlock:
+       mutex_unlock(&current->cred_exec_mutex);
+
+out_free:
        free_bprm(bprm);
 
 out_files:
@@ -1396,6 +1416,7 @@ EXPORT_SYMBOL(set_binfmt);
  */
 static int format_corename(char *corename, long signr)
 {
+       const struct cred *cred = current_cred();
        const char *pat_ptr = core_pattern;
        int ispipe = (*pat_ptr == '|');
        char *out_ptr = corename;
@@ -1432,7 +1453,7 @@ static int format_corename(char *corename, long signr)
                        /* uid */
                        case 'u':
                                rc = snprintf(out_ptr, out_end - out_ptr,
-                                             "%d", current->uid);
+                                             "%d", cred->uid);
                                if (rc > out_end - out_ptr)
                                        goto out;
                                out_ptr += rc;
@@ -1440,7 +1461,7 @@ static int format_corename(char *corename, long signr)
                        /* gid */
                        case 'g':
                                rc = snprintf(out_ptr, out_end - out_ptr,
-                                             "%d", current->gid);
+                                             "%d", cred->gid);
                                if (rc > out_end - out_ptr)
                                        goto out;
                                out_ptr += rc;
@@ -1716,8 +1737,9 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
        struct linux_binfmt * binfmt;
        struct inode * inode;
        struct file * file;
+       const struct cred *old_cred;
+       struct cred *cred;
        int retval = 0;
-       int fsuid = current->fsuid;
        int flag = 0;
        int ispipe = 0;
        unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
@@ -1730,12 +1752,20 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
        binfmt = current->binfmt;
        if (!binfmt || !binfmt->core_dump)
                goto fail;
+
+       cred = prepare_creds();
+       if (!cred) {
+               retval = -ENOMEM;
+               goto fail;
+       }
+
        down_write(&mm->mmap_sem);
        /*
         * If another thread got here first, or we are not dumpable, bail out.
         */
        if (mm->core_state || !get_dumpable(mm)) {
                up_write(&mm->mmap_sem);
+               put_cred(cred);
                goto fail;
        }
 
@@ -1746,12 +1776,16 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
         */
        if (get_dumpable(mm) == 2) {    /* Setuid core dump mode */
                flag = O_EXCL;          /* Stop rewrite attacks */
-               current->fsuid = 0;     /* Dump root private */
+               cred->fsuid = 0;        /* Dump root private */
        }
 
        retval = coredump_wait(exit_code, &core_state);
-       if (retval < 0)
+       if (retval < 0) {
+               put_cred(cred);
                goto fail;
+       }
+
+       old_cred = override_creds(cred);
 
        /*
         * Clear any false indication of pending signals that might
@@ -1823,7 +1857,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
         * Dont allow local users get cute and trick others to coredump
         * into their pre-created files:
         */
-       if (inode->i_uid != current->fsuid)
+       if (inode->i_uid != current_fsuid())
                goto close_fail;
        if (!file->f_op)
                goto close_fail;
@@ -1842,7 +1876,8 @@ fail_unlock:
        if (helper_argv)
                argv_free(helper_argv);
 
-       current->fsuid = fsuid;
+       revert_creds(old_cred);
+       put_cred(cred);
        coredump_finish(mm);
 fail:
        return retval;
index 890e018..197c7db 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/mount.h>
 #include <linux/namei.h>
+#include <linux/sched.h>
 
 #define dprintk(fmt, args...) do{}while(0)
 
@@ -249,6 +250,7 @@ static int filldir_one(void * __buf, const char * name, int len,
 static int get_name(struct vfsmount *mnt, struct dentry *dentry,
                char *name, struct dentry *child)
 {
+       const struct cred *cred = current_cred();
        struct inode *dir = dentry->d_inode;
        int error;
        struct file *file;
@@ -263,7 +265,7 @@ static int get_name(struct vfsmount *mnt, struct dentry *dentry,
        /*
         * Open the directory ...
         */
-       file = dentry_open(dget(dentry), mntget(mnt), O_RDONLY);
+       file = dentry_open(dget(dentry), mntget(mnt), O_RDONLY, cred);
        error = PTR_ERR(file);
        if (IS_ERR(file))
                goto out;
index 6dac7ba..4a29d63 100644 (file)
@@ -1193,7 +1193,7 @@ static int ext2_has_free_blocks(struct ext2_sb_info *sbi)
        free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter);
        root_blocks = le32_to_cpu(sbi->s_es->s_r_blocks_count);
        if (free_blocks < root_blocks + 1 && !capable(CAP_SYS_RESOURCE) &&
-               sbi->s_resuid != current->fsuid &&
+               sbi->s_resuid != current_fsuid() &&
                (sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) {
                return 0;
        }
index f597413..8d0add6 100644 (file)
@@ -550,7 +550,7 @@ got:
 
        sb->s_dirt = 1;
        mark_buffer_dirty(bh2);
-       inode->i_uid = current->fsuid;
+       inode->i_uid = current_fsuid();
        if (test_opt (sb, GRPID))
                inode->i_gid = dir->i_gid;
        else if (dir->i_mode & S_ISGID) {
@@ -558,7 +558,7 @@ got:
                if (S_ISDIR(mode))
                        mode |= S_ISGID;
        } else
-               inode->i_gid = current->fsgid;
+               inode->i_gid = current_fsgid();
        inode->i_mode = mode;
 
        inode->i_ino = ino;
index f5b57a2..0dbf1c0 100644 (file)
@@ -1422,7 +1422,7 @@ static int ext3_has_free_blocks(struct ext3_sb_info *sbi)
        free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter);
        root_blocks = le32_to_cpu(sbi->s_es->s_r_blocks_count);
        if (free_blocks < root_blocks + 1 && !capable(CAP_SYS_RESOURCE) &&
-               sbi->s_resuid != current->fsuid &&
+               sbi->s_resuid != current_fsuid() &&
                (sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) {
                return 0;
        }
index 47b678d..490bd0e 100644 (file)
@@ -539,7 +539,7 @@ got:
                percpu_counter_inc(&sbi->s_dirs_counter);
        sb->s_dirt = 1;
 
-       inode->i_uid = current->fsuid;
+       inode->i_uid = current_fsuid();
        if (test_opt (sb, GRPID))
                inode->i_gid = dir->i_gid;
        else if (dir->i_mode & S_ISGID) {
@@ -547,7 +547,7 @@ got:
                if (S_ISDIR(mode))
                        mode |= S_ISGID;
        } else
-               inode->i_gid = current->fsgid;
+               inode->i_gid = current_fsgid();
        inode->i_mode = mode;
 
        inode->i_ino = ino;
index db35cfd..38b3acf 100644 (file)
@@ -624,7 +624,7 @@ int ext4_has_free_blocks(struct ext4_sb_info *sbi, s64 nblocks)
                return 1;
 
        /* Hm, nope.  Are (enough) root reserved blocks available? */
-       if (sbi->s_resuid == current->fsuid ||
+       if (sbi->s_resuid == current_fsuid() ||
            ((sbi->s_resgid != 0) && in_group_p(sbi->s_resgid)) ||
            capable(CAP_SYS_RESOURCE)) {
                if (free_blocks >= (nblocks + dirty_blocks))
index 2a117e2..08cac9f 100644 (file)
@@ -787,7 +787,7 @@ got:
                spin_unlock(sb_bgl_lock(sbi, flex_group));
        }
 
-       inode->i_uid = current->fsuid;
+       inode->i_uid = current_fsuid();
        if (test_opt(sb, GRPID))
                inode->i_gid = dir->i_gid;
        else if (dir->i_mode & S_ISGID) {
@@ -795,7 +795,7 @@ got:
                if (S_ISDIR(mode))
                        mode |= S_ISGID;
        } else
-               inode->i_gid = current->fsgid;
+               inode->i_gid = current_fsgid();
        inode->i_mode = mode;
 
        inode->i_ino = ino + group * EXT4_INODES_PER_GROUP(sb);
index f06a4e5..0a7f4a9 100644 (file)
@@ -304,7 +304,7 @@ static int fat_allow_set_time(struct msdos_sb_info *sbi, struct inode *inode)
 {
        mode_t allow_utime = sbi->options.allow_utime;
 
-       if (current->fsuid != inode->i_uid) {
+       if (current_fsuid() != inode->i_uid) {
                if (in_group_p(inode->i_gid))
                        allow_utime >>= 3;
                if (allow_utime & MAY_WRITE)
index bdd8fb7..d937aaf 100644 (file)
@@ -926,8 +926,8 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug,
 
        opts->isvfat = is_vfat;
 
-       opts->fs_uid = current->uid;
-       opts->fs_gid = current->gid;
+       opts->fs_uid = current_uid();
+       opts->fs_gid = current_gid();
        opts->fs_fmask = opts->fs_dmask = current->fs->umask;
        opts->allow_utime = -1;
        opts->codepage = fat_default_codepage;
index 549daf8..cdc1419 100644 (file)
@@ -212,13 +212,14 @@ static void f_modown(struct file *filp, struct pid *pid, enum pid_type type,
 int __f_setown(struct file *filp, struct pid *pid, enum pid_type type,
                int force)
 {
+       const struct cred *cred = current_cred();
        int err;
        
        err = security_file_set_fowner(filp);
        if (err)
                return err;
 
-       f_modown(filp, pid, type, current->uid, current->euid, force);
+       f_modown(filp, pid, type, cred->uid, cred->euid, force);
        return 0;
 }
 EXPORT_SYMBOL(__f_setown);
@@ -407,10 +408,17 @@ static const long band_table[NSIGPOLL] = {
 static inline int sigio_perm(struct task_struct *p,
                              struct fown_struct *fown, int sig)
 {
-       return (((fown->euid == 0) ||
-                (fown->euid == p->suid) || (fown->euid == p->uid) ||
-                (fown->uid == p->suid) || (fown->uid == p->uid)) &&
-               !security_file_send_sigiotask(p, fown, sig));
+       const struct cred *cred;
+       int ret;
+
+       rcu_read_lock();
+       cred = __task_cred(p);
+       ret = ((fown->euid == 0 ||
+               fown->euid == cred->suid || fown->euid == cred->uid ||
+               fown->uid  == cred->suid || fown->uid  == cred->uid) &&
+              !security_file_send_sigiotask(p, fown, sig));
+       rcu_read_unlock();
+       return ret;
 }
 
 static void send_sigio_to_task(struct task_struct *p,
index 5ad0eca..0fbcacc 100644 (file)
@@ -36,7 +36,9 @@ static struct percpu_counter nr_files __cacheline_aligned_in_smp;
 
 static inline void file_free_rcu(struct rcu_head *head)
 {
-       struct file *f =  container_of(head, struct file, f_u.fu_rcuhead);
+       struct file *f = container_of(head, struct file, f_u.fu_rcuhead);
+
+       put_cred(f->f_cred);
        kmem_cache_free(filp_cachep, f);
 }
 
@@ -94,7 +96,7 @@ int proc_nr_files(ctl_table *table, int write, struct file *filp,
  */
 struct file *get_empty_filp(void)
 {
-       struct task_struct *tsk;
+       const struct cred *cred = current_cred();
        static int old_max;
        struct file * f;
 
@@ -118,12 +120,10 @@ struct file *get_empty_filp(void)
        if (security_file_alloc(f))
                goto fail_sec;
 
-       tsk = current;
        INIT_LIST_HEAD(&f->f_u.fu_list);
        atomic_long_set(&f->f_count, 1);
        rwlock_init(&f->f_owner.lock);
-       f->f_uid = tsk->fsuid;
-       f->f_gid = tsk->fsgid;
+       f->f_cred = get_cred(cred);
        eventpoll_init_file(f);
        /* f->f_version: 0 */
        return f;
index b723614..fba5716 100644 (file)
@@ -87,8 +87,8 @@ static void __fuse_put_request(struct fuse_req *req)
 
 static void fuse_req_init_context(struct fuse_req *req)
 {
-       req->in.h.uid = current->fsuid;
-       req->in.h.gid = current->fsgid;
+       req->in.h.uid = current_fsuid();
+       req->in.h.gid = current_fsgid();
        req->in.h.pid = current->pid;
 }
 
index fd03330..95bc22b 100644 (file)
@@ -869,18 +869,25 @@ int fuse_update_attributes(struct inode *inode, struct kstat *stat,
  */
 int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task)
 {
+       const struct cred *cred;
+       int ret;
+
        if (fc->flags & FUSE_ALLOW_OTHER)
                return 1;
 
-       if (task->euid == fc->user_id &&
-           task->suid == fc->user_id &&
-           task->uid == fc->user_id &&
-           task->egid == fc->group_id &&
-           task->sgid == fc->group_id &&
-           task->gid == fc->group_id)
-               return 1;
+       rcu_read_lock();
+       ret = 0;
+       cred = __task_cred(task);
+       if (cred->euid == fc->user_id &&
+           cred->suid == fc->user_id &&
+           cred->uid  == fc->user_id &&
+           cred->egid == fc->group_id &&
+           cred->sgid == fc->group_id &&
+           cred->gid  == fc->group_id)
+               ret = 1;
+       rcu_read_unlock();
 
-       return 0;
+       return ret;
 }
 
 static int fuse_access(struct inode *inode, int mask)
index 7cee695..d576168 100644 (file)
@@ -705,18 +705,18 @@ static void munge_mode_uid_gid(struct gfs2_inode *dip, unsigned int *mode,
            (dip->i_inode.i_mode & S_ISUID) && dip->i_inode.i_uid) {
                if (S_ISDIR(*mode))
                        *mode |= S_ISUID;
-               else if (dip->i_inode.i_uid != current->fsuid)
+               else if (dip->i_inode.i_uid != current_fsuid())
                        *mode &= ~07111;
                *uid = dip->i_inode.i_uid;
        } else
-               *uid = current->fsuid;
+               *uid = current_fsuid();
 
        if (dip->i_inode.i_mode & S_ISGID) {
                if (S_ISDIR(*mode))
                        *mode |= S_ISGID;
                *gid = dip->i_inode.i_gid;
        } else
-               *gid = current->fsgid;
+               *gid = current_fsgid();
 }
 
 static int alloc_dinode(struct gfs2_inode *dip, u64 *no_addr, u64 *generation)
@@ -1124,8 +1124,8 @@ int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name,
                return -EPERM;
 
        if ((dip->i_inode.i_mode & S_ISVTX) &&
-           dip->i_inode.i_uid != current->fsuid &&
-           ip->i_inode.i_uid != current->fsuid && !capable(CAP_FOWNER))
+           dip->i_inode.i_uid != current_fsuid() &&
+           ip->i_inode.i_uid != current_fsuid() && !capable(CAP_FOWNER))
                return -EPERM;
 
        if (IS_APPEND(&dip->i_inode))
index c69b7ac..9435dda 100644 (file)
@@ -155,8 +155,8 @@ struct inode *hfs_new_inode(struct inode *dir, struct qstr *name, int mode)
        hfs_cat_build_key(sb, (btree_key *)&HFS_I(inode)->cat_key, dir->i_ino, name);
        inode->i_ino = HFS_SB(sb)->next_id++;
        inode->i_mode = mode;
-       inode->i_uid = current->fsuid;
-       inode->i_gid = current->fsgid;
+       inode->i_uid = current_fsuid();
+       inode->i_gid = current_fsgid();
        inode->i_nlink = 1;
        inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
        HFS_I(inode)->flags = 0;
index 3c7c763..c8b5acf 100644 (file)
@@ -210,8 +210,8 @@ static int parse_options(char *options, struct hfs_sb_info *hsb)
        int tmp, token;
 
        /* initialize the sb with defaults */
-       hsb->s_uid = current->uid;
-       hsb->s_gid = current->gid;
+       hsb->s_uid = current_uid();
+       hsb->s_gid = current_gid();
        hsb->s_file_umask = 0133;
        hsb->s_dir_umask = 0022;
        hsb->s_type = hsb->s_creator = cpu_to_be32(0x3f3f3f3f); /* == '????' */
index b207f0e..f105ee9 100644 (file)
@@ -296,8 +296,8 @@ struct inode *hfsplus_new_inode(struct super_block *sb, int mode)
 
        inode->i_ino = HFSPLUS_SB(sb).next_cnid++;
        inode->i_mode = mode;
-       inode->i_uid = current->fsuid;
-       inode->i_gid = current->fsgid;
+       inode->i_uid = current_fsuid();
+       inode->i_gid = current_fsgid();
        inode->i_nlink = 1;
        inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
        INIT_LIST_HEAD(&HFSPLUS_I(inode).open_dir_list);
index 9699c56..bab7f8d 100644 (file)
@@ -49,8 +49,8 @@ void hfsplus_fill_defaults(struct hfsplus_sb_info *opts)
        opts->creator = HFSPLUS_DEF_CR_TYPE;
        opts->type = HFSPLUS_DEF_CR_TYPE;
        opts->umask = current->fs->umask;
-       opts->uid = current->uid;
-       opts->gid = current->gid;
+       opts->uid = current_uid();
+       opts->gid = current_gid();
        opts->part = -1;
        opts->session = -1;
 }
index 10783f3..b649232 100644 (file)
@@ -92,11 +92,11 @@ static int hpfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        inc_nlink(dir);
        insert_inode_hash(result);
 
-       if (result->i_uid != current->fsuid ||
-           result->i_gid != current->fsgid ||
+       if (result->i_uid != current_fsuid() ||
+           result->i_gid != current_fsgid() ||
            result->i_mode != (mode | S_IFDIR)) {
-               result->i_uid = current->fsuid;
-               result->i_gid = current->fsgid;
+               result->i_uid = current_fsuid();
+               result->i_gid = current_fsgid();
                result->i_mode = mode | S_IFDIR;
                hpfs_write_inode_nolock(result);
        }
@@ -184,11 +184,11 @@ static int hpfs_create(struct inode *dir, struct dentry *dentry, int mode, struc
 
        insert_inode_hash(result);
 
-       if (result->i_uid != current->fsuid ||
-           result->i_gid != current->fsgid ||
+       if (result->i_uid != current_fsuid() ||
+           result->i_gid != current_fsgid() ||
            result->i_mode != (mode | S_IFREG)) {
-               result->i_uid = current->fsuid;
-               result->i_gid = current->fsgid;
+               result->i_uid = current_fsuid();
+               result->i_gid = current_fsgid();
                result->i_mode = mode | S_IFREG;
                hpfs_write_inode_nolock(result);
        }
@@ -247,8 +247,8 @@ static int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t
        result->i_mtime.tv_nsec = 0;
        result->i_atime.tv_nsec = 0;
        hpfs_i(result)->i_ea_size = 0;
-       result->i_uid = current->fsuid;
-       result->i_gid = current->fsgid;
+       result->i_uid = current_fsuid();
+       result->i_gid = current_fsgid();
        result->i_nlink = 1;
        result->i_size = 0;
        result->i_blocks = 1;
@@ -325,8 +325,8 @@ static int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *sy
        result->i_atime.tv_nsec = 0;
        hpfs_i(result)->i_ea_size = 0;
        result->i_mode = S_IFLNK | 0777;
-       result->i_uid = current->fsuid;
-       result->i_gid = current->fsgid;
+       result->i_uid = current_fsuid();
+       result->i_gid = current_fsgid();
        result->i_blocks = 1;
        result->i_nlink = 1;
        result->i_size = strlen(symlink);
index 29ad461..0d049b8 100644 (file)
@@ -475,8 +475,8 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent)
 
        init_MUTEX(&sbi->hpfs_creation_de);
 
-       uid = current->uid;
-       gid = current->gid;
+       uid = current_uid();
+       gid = current_gid();
        umask = current->fs->umask;
        lowercase = 0;
        conv = CONV_BINARY;
index 2b3d182..b278f7f 100644 (file)
@@ -426,6 +426,7 @@ static int file_mode(int fmode)
 
 static int hppfs_open(struct inode *inode, struct file *file)
 {
+       const struct cred *cred = file->f_cred;
        struct hppfs_private *data;
        struct vfsmount *proc_mnt;
        struct dentry *proc_dentry;
@@ -446,7 +447,7 @@ static int hppfs_open(struct inode *inode, struct file *file)
 
        /* XXX This isn't closed anywhere */
        data->proc_file = dentry_open(dget(proc_dentry), mntget(proc_mnt),
-                                     file_mode(file->f_mode));
+                                     file_mode(file->f_mode), cred);
        err = PTR_ERR(data->proc_file);
        if (IS_ERR(data->proc_file))
                goto out_free1;
@@ -489,6 +490,7 @@ static int hppfs_open(struct inode *inode, struct file *file)
 
 static int hppfs_dir_open(struct inode *inode, struct file *file)
 {
+       const struct cred *cred = file->f_cred;
        struct hppfs_private *data;
        struct vfsmount *proc_mnt;
        struct dentry *proc_dentry;
@@ -502,7 +504,7 @@ static int hppfs_dir_open(struct inode *inode, struct file *file)
        proc_dentry = HPPFS_I(inode)->proc_dentry;
        proc_mnt = inode->i_sb->s_fs_info;
        data->proc_file = dentry_open(dget(proc_dentry), mntget(proc_mnt),
-                                     file_mode(file->f_mode));
+                                     file_mode(file->f_mode), cred);
        err = PTR_ERR(data->proc_file);
        if (IS_ERR(data->proc_file))
                goto out_free;
index 61edc70..7d479ce 100644 (file)
@@ -551,9 +551,9 @@ static int hugetlbfs_mknod(struct inode *dir,
                if (S_ISDIR(mode))
                        mode |= S_ISGID;
        } else {
-               gid = current->fsgid;
+               gid = current_fsgid();
        }
-       inode = hugetlbfs_get_inode(dir->i_sb, current->fsuid, gid, mode, dev);
+       inode = hugetlbfs_get_inode(dir->i_sb, current_fsuid(), gid, mode, dev);
        if (inode) {
                dir->i_ctime = dir->i_mtime = CURRENT_TIME;
                d_instantiate(dentry, inode);
@@ -586,9 +586,9 @@ static int hugetlbfs_symlink(struct inode *dir,
        if (dir->i_mode & S_ISGID)
                gid = dir->i_gid;
        else
-               gid = current->fsgid;
+               gid = current_fsgid();
 
-       inode = hugetlbfs_get_inode(dir->i_sb, current->fsuid,
+       inode = hugetlbfs_get_inode(dir->i_sb, current_fsuid(),
                                        gid, S_IFLNK|S_IRWXUGO, 0);
        if (inode) {
                int l = strlen(symname)+1;
@@ -854,8 +854,8 @@ hugetlbfs_fill_super(struct super_block *sb, void *data, int silent)
 
        config.nr_blocks = -1; /* No limit on size by default */
        config.nr_inodes = -1; /* No limit on number of inodes by default */
-       config.uid = current->fsuid;
-       config.gid = current->fsgid;
+       config.uid = current_fsuid();
+       config.gid = current_fsgid();
        config.mode = 0755;
        config.hstate = &default_hstate;
        ret = hugetlbfs_parse_options(data, &config);
@@ -951,6 +951,7 @@ struct file *hugetlb_file_setup(const char *name, size_t size)
        struct inode *inode;
        struct dentry *dentry, *root;
        struct qstr quick_string;
+       struct user_struct *user = current_user();
 
        if (!hugetlbfs_vfsmount)
                return ERR_PTR(-ENOENT);
@@ -958,7 +959,7 @@ struct file *hugetlb_file_setup(const char *name, size_t size)
        if (!can_do_hugetlb_shm())
                return ERR_PTR(-EPERM);
 
-       if (!user_shm_lock(size, current->user))
+       if (!user_shm_lock(size, user))
                return ERR_PTR(-ENOMEM);
 
        root = hugetlbfs_vfsmount->mnt_root;
@@ -970,8 +971,8 @@ struct file *hugetlb_file_setup(const char *name, size_t size)
                goto out_shm_unlock;
 
        error = -ENOSPC;
-       inode = hugetlbfs_get_inode(root->d_sb, current->fsuid,
-                               current->fsgid, S_IFREG | S_IRWXUGO, 0);
+       inode = hugetlbfs_get_inode(root->d_sb, current_fsuid(),
+                               current_fsgid(), S_IFREG | S_IRWXUGO, 0);
        if (!inode)
                goto out_dentry;
 
@@ -998,7 +999,7 @@ out_inode:
 out_dentry:
        dput(dentry);
 out_shm_unlock:
-       user_shm_unlock(size, current->user);
+       user_shm_unlock(size, user);
        return ERR_PTR(error);
 }
 
index d367e9b..e2425bb 100644 (file)
@@ -601,7 +601,7 @@ asmlinkage long sys_inotify_init1(int flags)
                goto out_put_fd;
        }
 
-       user = get_uid(current->user);
+       user = get_current_user();
        if (unlikely(atomic_read(&user->inotify_devs) >=
                        inotify_max_user_instances)) {
                ret = -EMFILE;
index 80aa9a0..53af885 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 struct super_block;
+struct linux_binprm;
 
 /*
  * block_dev.c
@@ -39,6 +40,11 @@ static inline int sb_is_blkdev_sb(struct super_block *sb)
  */
 extern void __init chrdev_init(void);
 
+/*
+ * exec.c
+ */
+extern void check_unsafe_exec(struct linux_binprm *);
+
 /*
  * namespace.c
  */
index da3cc46..3569e0a 100644 (file)
@@ -31,10 +31,16 @@ static int set_task_ioprio(struct task_struct *task, int ioprio)
 {
        int err;
        struct io_context *ioc;
+       const struct cred *cred = current_cred(), *tcred;
 
-       if (task->uid != current->euid &&
-           task->uid != current->uid && !capable(CAP_SYS_NICE))
+       rcu_read_lock();
+       tcred = __task_cred(task);
+       if (tcred->uid != cred->euid &&
+           tcred->uid != cred->uid && !capable(CAP_SYS_NICE)) {
+               rcu_read_unlock();
                return -EPERM;
+       }
+       rcu_read_unlock();
 
        err = security_task_setioprio(task, ioprio);
        if (err)
@@ -123,7 +129,7 @@ asmlinkage long sys_ioprio_set(int which, int who, int ioprio)
                        break;
                case IOPRIO_WHO_USER:
                        if (!who)
-                               user = current->user;
+                               user = current_user();
                        else
                                user = find_user(who);
 
@@ -131,7 +137,7 @@ asmlinkage long sys_ioprio_set(int which, int who, int ioprio)
                                break;
 
                        do_each_thread(g, p) {
-                               if (p->uid != who)
+                               if (__task_cred(p)->uid != who)
                                        continue;
                                ret = set_task_ioprio(p, ioprio);
                                if (ret)
@@ -216,7 +222,7 @@ asmlinkage long sys_ioprio_get(int which, int who)
                        break;
                case IOPRIO_WHO_USER:
                        if (!who)
-                               user = current->user;
+                               user = current_user();
                        else
                                user = find_user(who);
 
@@ -224,7 +230,7 @@ asmlinkage long sys_ioprio_get(int which, int who)
                                break;
 
                        do_each_thread(g, p) {
-                               if (p->uid != user->uid)
+                               if (__task_cred(p)->uid != user->uid)
                                        continue;
                                tmpio = get_task_ioprio(p);
                                if (tmpio < 0)
index ed6574b..70022fd 100644 (file)
@@ -93,13 +93,13 @@ struct inode *ialloc(struct inode *parent, umode_t mode)
                return ERR_PTR(rc);
        }
 
-       inode->i_uid = current->fsuid;
+       inode->i_uid = current_fsuid();
        if (parent->i_mode & S_ISGID) {
                inode->i_gid = parent->i_gid;
                if (S_ISDIR(mode))
                        mode |= S_ISGID;
        } else
-               inode->i_gid = current->fsgid;
+               inode->i_gid = current_fsgid();
 
        /*
         * New inodes need to save sane values on disk when
index 09062e3..46a2e12 100644 (file)
@@ -1349,7 +1349,7 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
        struct inode *inode = dentry->d_inode;
        int error, rdlease_count = 0, wrlease_count = 0;
 
-       if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE))
+       if ((current_fsuid() != inode->i_uid) && !capable(CAP_LEASE))
                return -EACCES;
        if (!S_ISREG(inode->i_mode))
                return -EINVAL;
index 703cc35..3aebe32 100644 (file)
@@ -262,8 +262,8 @@ struct inode * minix_new_inode(const struct inode * dir, int * error)
                iput(inode);
                return NULL;
        }
-       inode->i_uid = current->fsuid;
-       inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+       inode->i_uid = current_fsuid();
+       inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current_fsgid();
        inode->i_ino = j;
        inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
        inode->i_blocks = 0;
index d34e0f9..af3783f 100644 (file)
@@ -186,7 +186,7 @@ int generic_permission(struct inode *inode, int mask,
 
        mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
 
-       if (current->fsuid == inode->i_uid)
+       if (current_fsuid() == inode->i_uid)
                mode >>= 6;
        else {
                if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
@@ -441,7 +441,7 @@ static int exec_permission_lite(struct inode *inode)
        if (inode->i_op && inode->i_op->permission)
                return -EAGAIN;
 
-       if (current->fsuid == inode->i_uid)
+       if (current_fsuid() == inode->i_uid)
                mode >>= 6;
        else if (in_group_p(inode->i_gid))
                mode >>= 3;
@@ -1334,11 +1334,13 @@ static int user_path_parent(int dfd, const char __user *path,
  */
 static inline int check_sticky(struct inode *dir, struct inode *inode)
 {
+       uid_t fsuid = current_fsuid();
+
        if (!(dir->i_mode & S_ISVTX))
                return 0;
-       if (inode->i_uid == current->fsuid)
+       if (inode->i_uid == fsuid)
                return 0;
-       if (dir->i_uid == current->fsuid)
+       if (dir->i_uid == fsuid)
                return 0;
        return !capable(CAP_FOWNER);
 }
index 65b3dc8..1c09cab 100644 (file)
@@ -1176,7 +1176,7 @@ static int mount_is_safe(struct path *path)
        if (S_ISLNK(path->dentry->d_inode->i_mode))
                return -EPERM;
        if (path->dentry->d_inode->i_mode & S_ISVTX) {
-               if (current->uid != path->dentry->d_inode->i_uid)
+               if (current_uid() != path->dentry->d_inode->i_uid)
                        return -EPERM;
        }
        if (inode_permission(path->dentry->d_inode, MAY_WRITE))
index 3a97c95..6d04e05 100644 (file)
@@ -40,10 +40,10 @@ ncp_get_fs_info(struct ncp_server * server, struct file *file,
        struct inode *inode = file->f_path.dentry->d_inode;
        struct ncp_fs_info info;
 
-       if ((file_permission(file, MAY_WRITE) != 0)
-           && (current->uid != server->m.mounted_uid)) {
+       if (file_permission(file, MAY_WRITE) != 0
+           && current_uid() != server->m.mounted_uid)
                return -EACCES;
-       }
+
        if (copy_from_user(&info, arg, sizeof(info)))
                return -EFAULT;
 
@@ -70,10 +70,10 @@ ncp_get_fs_info_v2(struct ncp_server * server, struct file *file,
        struct inode *inode = file->f_path.dentry->d_inode;
        struct ncp_fs_info_v2 info2;
 
-       if ((file_permission(file, MAY_WRITE) != 0)
-           && (current->uid != server->m.mounted_uid)) {
+       if (file_permission(file, MAY_WRITE) != 0
+           && current_uid() != server->m.mounted_uid)
                return -EACCES;
-       }
+
        if (copy_from_user(&info2, arg, sizeof(info2)))
                return -EFAULT;
 
@@ -141,10 +141,10 @@ ncp_get_compat_fs_info_v2(struct ncp_server * server, struct file *file,
        struct inode *inode = file->f_path.dentry->d_inode;
        struct compat_ncp_fs_info_v2 info2;
 
-       if ((file_permission(file, MAY_WRITE) != 0)
-           && (current->uid != server->m.mounted_uid)) {
+       if (file_permission(file, MAY_WRITE) != 0
+           && current_uid() != server->m.mounted_uid)
                return -EACCES;
-       }
+
        if (copy_from_user(&info2, arg, sizeof(info2)))
                return -EFAULT;
 
@@ -270,16 +270,17 @@ static int __ncp_ioctl(struct inode *inode, struct file *filp,
        struct ncp_ioctl_request request;
        char* bouncebuffer;
        void __user *argp = (void __user *)arg;
+       uid_t uid = current_uid();
 
        switch (cmd) {
 #ifdef CONFIG_COMPAT
        case NCP_IOC_NCPREQUEST_32:
 #endif
        case NCP_IOC_NCPREQUEST:
-               if ((file_permission(filp, MAY_WRITE) != 0)
-                   && (current->uid != server->m.mounted_uid)) {
+               if (file_permission(filp, MAY_WRITE) != 0
+                   && uid != server->m.mounted_uid)
                        return -EACCES;
-               }
+
 #ifdef CONFIG_COMPAT
                if (cmd == NCP_IOC_NCPREQUEST_32) {
                        struct compat_ncp_ioctl_request request32;
@@ -356,10 +357,10 @@ static int __ncp_ioctl(struct inode *inode, struct file *filp,
        case NCP_IOC_GETMOUNTUID16:
        case NCP_IOC_GETMOUNTUID32:
        case NCP_IOC_GETMOUNTUID64:
-               if ((file_permission(filp, MAY_READ) != 0)
-                       && (current->uid != server->m.mounted_uid)) {
+               if (file_permission(filp, MAY_READ) != 0
+                       && uid != server->m.mounted_uid)
                        return -EACCES;
-               }
+
                if (cmd == NCP_IOC_GETMOUNTUID16) {
                        u16 uid;
                        SET_UID(uid, server->m.mounted_uid);
@@ -380,11 +381,10 @@ static int __ncp_ioctl(struct inode *inode, struct file *filp,
                {
                        struct ncp_setroot_ioctl sr;
 
-                       if ((file_permission(filp, MAY_READ) != 0)
-                           && (current->uid != server->m.mounted_uid))
-                       {
+                       if (file_permission(filp, MAY_READ) != 0
+                           && uid != server->m.mounted_uid)
                                return -EACCES;
-                       }
+
                        if (server->m.mounted_vol[0]) {
                                struct dentry* dentry = inode->i_sb->s_root;
 
@@ -408,6 +408,7 @@ static int __ncp_ioctl(struct inode *inode, struct file *filp,
                                return -EFAULT;
                        return 0;
                }
+
        case NCP_IOC_SETROOT:
                {
                        struct ncp_setroot_ioctl sr;
@@ -455,11 +456,10 @@ static int __ncp_ioctl(struct inode *inode, struct file *filp,
 
 #ifdef CONFIG_NCPFS_PACKET_SIGNING     
        case NCP_IOC_SIGN_INIT:
-               if ((file_permission(filp, MAY_WRITE) != 0)
-                   && (current->uid != server->m.mounted_uid))
-               {
+               if (file_permission(filp, MAY_WRITE) != 0
+                   && uid != server->m.mounted_uid)
                        return -EACCES;
-               }
+
                if (argp) {
                        if (server->sign_wanted)
                        {
@@ -478,24 +478,22 @@ static int __ncp_ioctl(struct inode *inode, struct file *filp,
                return 0;               
                
         case NCP_IOC_SIGN_WANTED:
-               if ((file_permission(filp, MAY_READ) != 0)
-                   && (current->uid != server->m.mounted_uid))
-               {
+               if (file_permission(filp, MAY_READ) != 0
+                   && uid != server->m.mounted_uid)
                        return -EACCES;
-               }
                
                 if (put_user(server->sign_wanted, (int __user *)argp))
                        return -EFAULT;
                 return 0;
+
        case NCP_IOC_SET_SIGN_WANTED:
                {
                        int newstate;
 
-                       if ((file_permission(filp, MAY_WRITE) != 0)
-                           && (current->uid != server->m.mounted_uid))
-                       {
+                       if (file_permission(filp, MAY_WRITE) != 0
+                           && uid != server->m.mounted_uid)
                                return -EACCES;
-                       }
+
                        /* get only low 8 bits... */
                        if (get_user(newstate, (unsigned char __user *)argp))
                                return -EFAULT;
@@ -512,11 +510,10 @@ static int __ncp_ioctl(struct inode *inode, struct file *filp,
 
 #ifdef CONFIG_NCPFS_IOCTL_LOCKING
        case NCP_IOC_LOCKUNLOCK:
-               if ((file_permission(filp, MAY_WRITE) != 0)
-                   && (current->uid != server->m.mounted_uid))
-               {
+               if (file_permission(filp, MAY_WRITE) != 0
+                   && uid != server->m.mounted_uid)
                        return -EACCES;
-               }
+
                {
                        struct ncp_lock_ioctl    rqdata;
 
@@ -585,9 +582,8 @@ outrel:
 
 #ifdef CONFIG_COMPAT
        case NCP_IOC_GETOBJECTNAME_32:
-               if (current->uid != server->m.mounted_uid) {
+               if (uid != server->m.mounted_uid)
                        return -EACCES;
-               }
                {
                        struct compat_ncp_objectname_ioctl user;
                        size_t outl;
@@ -609,10 +605,10 @@ outrel:
                        return 0;
                }
 #endif
+
        case NCP_IOC_GETOBJECTNAME:
-               if (current->uid != server->m.mounted_uid) {
+               if (uid != server->m.mounted_uid)
                        return -EACCES;
-               }
                {
                        struct ncp_objectname_ioctl user;
                        size_t outl;
@@ -633,13 +629,13 @@ outrel:
                                return -EFAULT;
                        return 0;
                }
+
 #ifdef CONFIG_COMPAT
        case NCP_IOC_SETOBJECTNAME_32:
 #endif
        case NCP_IOC_SETOBJECTNAME:
-               if (current->uid != server->m.mounted_uid) {
+               if (uid != server->m.mounted_uid)
                        return -EACCES;
-               }
                {
                        struct ncp_objectname_ioctl user;
                        void* newname;
@@ -691,13 +687,13 @@ outrel:
                        kfree(oldname);
                        return 0;
                }
+
 #ifdef CONFIG_COMPAT
        case NCP_IOC_GETPRIVATEDATA_32:
 #endif
        case NCP_IOC_GETPRIVATEDATA:
-               if (current->uid != server->m.mounted_uid) {
+               if (uid != server->m.mounted_uid)
                        return -EACCES;
-               }
                {
                        struct ncp_privatedata_ioctl user;
                        size_t outl;
@@ -736,13 +732,13 @@ outrel:
 
                        return 0;
                }
+
 #ifdef CONFIG_COMPAT
        case NCP_IOC_SETPRIVATEDATA_32:
 #endif
        case NCP_IOC_SETPRIVATEDATA:
-               if (current->uid != server->m.mounted_uid) {
+               if (uid != server->m.mounted_uid)
                        return -EACCES;
-               }
                {
                        struct ncp_privatedata_ioctl user;
                        void* new;
@@ -794,9 +790,10 @@ outrel:
 #endif /* CONFIG_NCPFS_NLS */
 
        case NCP_IOC_SETDENTRYTTL:
-               if ((file_permission(filp, MAY_WRITE) != 0) &&
-                                (current->uid != server->m.mounted_uid))
+               if (file_permission(filp, MAY_WRITE) != 0 &&
+                   uid != server->m.mounted_uid)
                        return -EACCES;
+
                {
                        u_int32_t user;
 
index aed8145..b1acbd6 100644 (file)
@@ -10,6 +10,8 @@
 #include <linux/sunrpc/svc.h>
 #include <linux/nfsd/nfsd.h>
 #include <linux/nfsd/syscall.h>
+#include <linux/cred.h>
+#include <linux/sched.h>
 #include <linux/linkage.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
@@ -41,7 +43,8 @@ static struct file *do_open(char *name, int flags)
                error = may_open(&nd, MAY_WRITE, FMODE_WRITE);
 
        if (!error)
-               return dentry_open(nd.path.dentry, nd.path.mnt, flags);
+               return dentry_open(nd.path.dentry, nd.path.mnt, flags,
+                                  current_cred());
 
        path_put(&nd.path);
        return ERR_PTR(error);
index 294992e..0184fe9 100644 (file)
@@ -27,53 +27,70 @@ int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp)
 
 int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
 {
-       struct svc_cred cred = rqstp->rq_cred;
+       struct group_info *rqgi;
+       struct group_info *gi;
+       struct cred *new;
        int i;
        int flags = nfsexp_flags(rqstp, exp);
        int ret;
 
+       /* discard any old override before preparing the new set */
+       revert_creds(get_cred(current->real_cred));
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+
+       new->fsuid = rqstp->rq_cred.cr_uid;
+       new->fsgid = rqstp->rq_cred.cr_gid;
+
+       rqgi = rqstp->rq_cred.cr_group_info;
+
        if (flags & NFSEXP_ALLSQUASH) {
-               cred.cr_uid = exp->ex_anon_uid;
-               cred.cr_gid = exp->ex_anon_gid;
-               cred.cr_group_info = groups_alloc(0);
+               new->fsuid = exp->ex_anon_uid;
+               new->fsgid = exp->ex_anon_gid;
+               gi = groups_alloc(0);
        } else if (flags & NFSEXP_ROOTSQUASH) {
-               struct group_info *gi;
-               if (!cred.cr_uid)
-                       cred.cr_uid = exp->ex_anon_uid;
-               if (!cred.cr_gid)
-                       cred.cr_gid = exp->ex_anon_gid;
-               gi = groups_alloc(cred.cr_group_info->ngroups);
-               if (gi)
-                       for (i = 0; i < cred.cr_group_info->ngroups; i++) {
-                               if (!GROUP_AT(cred.cr_group_info, i))
-                                       GROUP_AT(gi, i) = exp->ex_anon_gid;
-                               else
-                                       GROUP_AT(gi, i) = GROUP_AT(cred.cr_group_info, i);
-                       }
-               cred.cr_group_info = gi;
-       } else
-               get_group_info(cred.cr_group_info);
-
-       if (cred.cr_uid != (uid_t) -1)
-               current->fsuid = cred.cr_uid;
-       else
-               current->fsuid = exp->ex_anon_uid;
-       if (cred.cr_gid != (gid_t) -1)
-               current->fsgid = cred.cr_gid;
-       else
-               current->fsgid = exp->ex_anon_gid;
+               if (!new->fsuid)
+                       new->fsuid = exp->ex_anon_uid;
+               if (!new->fsgid)
+                       new->fsgid = exp->ex_anon_gid;
 
-       if (!cred.cr_group_info)
-               return -ENOMEM;
-       ret = set_current_groups(cred.cr_group_info);
-       put_group_info(cred.cr_group_info);
-       if ((cred.cr_uid)) {
-               current->cap_effective =
-                       cap_drop_nfsd_set(current->cap_effective);
+               gi = groups_alloc(rqgi->ngroups);
+               if (!gi)
+                       goto oom;
+
+               for (i = 0; i < rqgi->ngroups; i++) {
+                       if (!GROUP_AT(rqgi, i))
+                               GROUP_AT(gi, i) = exp->ex_anon_gid;
+                       else
+                               GROUP_AT(gi, i) = GROUP_AT(rqgi, i);
+               }
        } else {
-               current->cap_effective =
-                       cap_raise_nfsd_set(current->cap_effective,
-                                          current->cap_permitted);
+               gi = get_group_info(rqgi);
        }
+
+       if (new->fsuid == (uid_t) -1)
+               new->fsuid = exp->ex_anon_uid;
+       if (new->fsgid == (gid_t) -1)
+               new->fsgid = exp->ex_anon_gid;
+
+       ret = set_groups(new, gi);
+       put_group_info(gi);
+       if (!ret)
+               goto error;
+
+       if (new->uid)
+               new->cap_effective = cap_drop_nfsd_set(new->cap_effective);
+       else
+               new->cap_effective = cap_raise_nfsd_set(new->cap_effective,
+                                                       new->cap_permitted);
+       put_cred(override_creds(new));
+       return 0;
+
+oom:
+       ret = -ENOMEM;
+error:
+       abort_creds(new);
        return ret;
 }
+
index b79ec93..0f9d6ef 100644 (file)
 static struct path rec_dir;
 static int rec_dir_init = 0;
 
-static void
-nfs4_save_user(uid_t *saveuid, gid_t *savegid)
+static int
+nfs4_save_creds(const struct cred **original_creds)
 {
-       *saveuid = current->fsuid;
-       *savegid = current->fsgid;
-       current->fsuid = 0;
-       current->fsgid = 0;
+       struct cred *new;
+
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+
+       new->fsuid = 0;
+       new->fsgid = 0;
+       *original_creds = override_creds(new);
+       put_cred(new);
+       return 0;
 }
 
 static void
-nfs4_reset_user(uid_t saveuid, gid_t savegid)
+nfs4_reset_creds(const struct cred *original)
 {
-       current->fsuid = saveuid;
-       current->fsgid = savegid;
+       revert_creds(original);
 }
 
 static void
@@ -129,10 +135,9 @@ nfsd4_sync_rec_dir(void)
 int
 nfsd4_create_clid_dir(struct nfs4_client *clp)
 {
+       const struct cred *original_cred;
        char *dname = clp->cl_recdir;
        struct dentry *dentry;
-       uid_t uid;
-       gid_t gid;
        int status;
 
        dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname);
@@ -140,7 +145,9 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
        if (!rec_dir_init || clp->cl_firststate)
                return 0;
 
-       nfs4_save_user(&uid, &gid);
+       status = nfs4_save_creds(&original_cred);
+       if (status < 0)
+               return status;
 
        /* lock the parent */
        mutex_lock(&rec_dir.dentry->d_inode->i_mutex);
@@ -168,7 +175,7 @@ out_unlock:
                clp->cl_firststate = 1;
                nfsd4_sync_rec_dir();
        }
-       nfs4_reset_user(uid, gid);
+       nfs4_reset_creds(original_cred);
        dprintk("NFSD: nfsd4_create_clid_dir returns %d\n", status);
        return status;
 }
@@ -211,26 +218,29 @@ nfsd4_build_dentrylist(void *arg, const char *name, int namlen,
 static int
 nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f)
 {
+       const struct cred *original_cred;
        struct file *filp;
        struct dentry_list_arg dla = {
                .parent = dir,
        };
        struct list_head *dentries = &dla.dentries;
        struct dentry_list *child;
-       uid_t uid;
-       gid_t gid;
        int status;
 
        if (!rec_dir_init)
                return 0;
 
-       nfs4_save_user(&uid, &gid);
+       status = nfs4_save_creds(&original_cred);
+       if (status < 0)
+               return status;
        INIT_LIST_HEAD(dentries);
 
-       filp = dentry_open(dget(dir), mntget(rec_dir.mnt), O_RDONLY);
+       filp = dentry_open(dget(dir), mntget(rec_dir.mnt), O_RDONLY,
+                          current_cred());
        status = PTR_ERR(filp);
        if (IS_ERR(filp))
                goto out;
+       INIT_LIST_HEAD(dentries);
        status = vfs_readdir(filp, nfsd4_build_dentrylist, &dla);
        fput(filp);
        while (!list_empty(dentries)) {
@@ -249,7 +259,7 @@ out:
                dput(child->dentry);
                kfree(child);
        }
-       nfs4_reset_user(uid, gid);
+       nfs4_reset_creds(original_cred);
        return status;
 }
 
@@ -311,8 +321,7 @@ out:
 void
 nfsd4_remove_clid_dir(struct nfs4_client *clp)
 {
-       uid_t uid;
-       gid_t gid;
+       const struct cred *original_cred;
        int status;
 
        if (!rec_dir_init || !clp->cl_firststate)
@@ -322,9 +331,13 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp)
        if (status)
                goto out;
        clp->cl_firststate = 0;
-       nfs4_save_user(&uid, &gid);
+
+       status = nfs4_save_creds(&original_cred);
+       if (status < 0)
+               goto out;
+
        status = nfsd4_unlink_clid_dir(clp->cl_recdir, HEXDIR_LEN-1);
-       nfs4_reset_user(uid, gid);
+       nfs4_reset_creds(original_cred);
        if (status == 0)
                nfsd4_sync_rec_dir();
        mnt_drop_write(rec_dir.mnt);
@@ -401,16 +414,21 @@ nfsd4_recdir_load(void) {
 void
 nfsd4_init_recdir(char *rec_dirname)
 {
-       uid_t                   uid = 0;
-       gid_t                   gid = 0;
-       int                     status;
+       const struct cred *original_cred;
+       int status;
 
        printk("NFSD: Using %s as the NFSv4 state recovery directory\n",
                        rec_dirname);
 
        BUG_ON(rec_dir_init);
 
-       nfs4_save_user(&uid, &gid);
+       status = nfs4_save_creds(&original_cred);
+       if (status < 0) {
+               printk("NFSD: Unable to change credentials to find recovery"
+                      " directory: error %d\n",
+                      status);
+               return;
+       }
 
        status = kern_path(rec_dirname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
                        &rec_dir);
@@ -420,7 +438,7 @@ nfsd4_init_recdir(char *rec_dirname)
 
        if (!status)
                rec_dir_init = 1;
-       nfs4_reset_user(uid, gid);
+       nfs4_reset_creds(original_cred);
 }
 
 void
index cd25d91..f0da7d9 100644 (file)
@@ -186,9 +186,14 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
                 * access control settings being in effect, we cannot
                 * fix that case easily.
                 */
-               current->cap_effective =
-                       cap_raise_nfsd_set(current->cap_effective,
-                                          current->cap_permitted);
+               struct cred *new = prepare_creds();
+               if (!new)
+                       return nfserrno(-ENOMEM);
+               new->cap_effective =
+                       cap_raise_nfsd_set(new->cap_effective,
+                                          new->cap_permitted);
+               put_cred(override_creds(new));
+               put_cred(new);
        } else {
                error = nfsd_setuser_and_check_port(rqstp, exp);
                if (error)
index 4433c8f..d1c5f78 100644 (file)
@@ -671,6 +671,7 @@ __be32
 nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
                        int access, struct file **filp)
 {
+       const struct cred *cred = current_cred();
        struct dentry   *dentry;
        struct inode    *inode;
        int             flags = O_RDONLY|O_LARGEFILE;
@@ -725,7 +726,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
                DQUOT_INIT(inode);
        }
        *filp = dentry_open(dget(dentry), mntget(fhp->fh_export->ex_path.mnt),
-                               flags);
+                           flags, cred);
        if (IS_ERR(*filp))
                host_err = PTR_ERR(*filp);
 out_nfserr:
@@ -1169,7 +1170,7 @@ nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *resfhp,
         * send along the gid on create when it tries to implement
         * setgid directories via NFS:
         */
-       if (current->fsuid != 0)
+       if (current_fsuid() != 0)
                iap->ia_valid &= ~(ATTR_UID|ATTR_GID);
        if (iap->ia_valid)
                return nfsd_setattr(rqstp, resfhp, iap, 0, (time_t)0);
@@ -2001,7 +2002,7 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
                IS_APPEND(inode)?       " append" : "",
                __mnt_is_readonly(exp->ex_path.mnt)?    " ro" : "");
        dprintk("      owner %d/%d user %d/%d\n",
-               inode->i_uid, inode->i_gid, current->fsuid, current->fsgid);
+               inode->i_uid, inode->i_gid, current_fsuid(), current_fsgid());
 #endif
 
        /* Normally we reject any write/sattr etc access on a read-only file
@@ -2044,7 +2045,7 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
         * with NFSv3.
         */
        if ((acc & NFSD_MAY_OWNER_OVERRIDE) &&
-           inode->i_uid == current->fsuid)
+           inode->i_uid == current_fsuid())
                return 0;
 
        /* This assumes  NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */
index ba962d7..6f7a77d 100644 (file)
@@ -339,8 +339,8 @@ static struct inode *dlmfs_get_root_inode(struct super_block *sb)
                ip = DLMFS_I(inode);
 
                inode->i_mode = mode;
-               inode->i_uid = current->fsuid;
-               inode->i_gid = current->fsgid;
+               inode->i_uid = current_fsuid();
+               inode->i_gid = current_fsgid();
                inode->i_blocks = 0;
                inode->i_mapping->backing_dev_info = &dlmfs_backing_dev_info;
                inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
@@ -365,8 +365,8 @@ static struct inode *dlmfs_get_inode(struct inode *parent,
                return NULL;
 
        inode->i_mode = mode;
-       inode->i_uid = current->fsuid;
-       inode->i_gid = current->fsgid;
+       inode->i_uid = current_fsuid();
+       inode->i_gid = current_fsgid();
        inode->i_blocks = 0;
        inode->i_mapping->backing_dev_info = &dlmfs_backing_dev_info;
        inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
index f4967e6..2545e74 100644 (file)
@@ -421,13 +421,13 @@ static int ocfs2_mknod_locked(struct ocfs2_super *osb,
        fe->i_blkno = cpu_to_le64(fe_blkno);
        fe->i_suballoc_bit = cpu_to_le16(suballoc_bit);
        fe->i_suballoc_slot = cpu_to_le16(inode_ac->ac_alloc_slot);
-       fe->i_uid = cpu_to_le32(current->fsuid);
+       fe->i_uid = cpu_to_le32(current_fsuid());
        if (dir->i_mode & S_ISGID) {
                fe->i_gid = cpu_to_le32(dir->i_gid);
                if (S_ISDIR(mode))
                        mode |= S_ISGID;
        } else
-               fe->i_gid = cpu_to_le32(current->fsgid);
+               fe->i_gid = cpu_to_le32(current_fsgid());
        fe->i_mode = cpu_to_le16(mode);
        if (S_ISCHR(mode) || S_ISBLK(mode))
                fe->id1.dev1.i_rdev = cpu_to_le64(huge_encode_dev(dev));
index cbf047a..6afe57c 100644 (file)
@@ -37,8 +37,8 @@ struct inode *omfs_new_inode(struct inode *dir, int mode)
 
        inode->i_ino = new_block;
        inode->i_mode = mode;
-       inode->i_uid = current->fsuid;
-       inode->i_gid = current->fsgid;
+       inode->i_uid = current_fsuid();
+       inode->i_gid = current_fsgid();
        inode->i_blocks = 0;
        inode->i_mapping->a_ops = &omfs_aops;
 
@@ -420,8 +420,8 @@ static int omfs_fill_super(struct super_block *sb, void *data, int silent)
 
        sb->s_fs_info = sbi;
 
-       sbi->s_uid = current->uid;
-       sbi->s_gid = current->gid;
+       sbi->s_uid = current_uid();
+       sbi->s_gid = current_gid();
        sbi->s_dmask = sbi->s_fmask = current->fs->umask;
 
        if (!parse_options((char *) data, sbi))
index 83cdb9d..c0a426d 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -425,39 +425,33 @@ out:
  */
 asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
 {
+       const struct cred *old_cred;
+       struct cred *override_cred;
        struct path path;
        struct inode *inode;
-       int old_fsuid, old_fsgid;
-       kernel_cap_t uninitialized_var(old_cap);  /* !SECURE_NO_SETUID_FIXUP */
        int res;
 
        if (mode & ~S_IRWXO)    /* where's F_OK, X_OK, W_OK, R_OK? */
                return -EINVAL;
 
-       old_fsuid = current->fsuid;
-       old_fsgid = current->fsgid;
+       override_cred = prepare_creds();
+       if (!override_cred)
+               return -ENOMEM;
 
-       current->fsuid = current->uid;
-       current->fsgid = current->gid;
+       override_cred->fsuid = override_cred->uid;
+       override_cred->fsgid = override_cred->gid;
 
        if (!issecure(SECURE_NO_SETUID_FIXUP)) {
-               /*
-                * Clear the capabilities if we switch to a non-root user
-                */
-#ifndef CONFIG_SECURITY_FILE_CAPABILITIES
-               /*
-                * FIXME: There is a race here against sys_capset.  The
-                * capabilities can change yet we will restore the old
-                * value below.  We should hold task_capabilities_lock,
-                * but we cannot because user_path_at can sleep.
-                */
-#endif /* ndef CONFIG_SECURITY_FILE_CAPABILITIES */
-               if (current->uid)
-                       old_cap = cap_set_effective(__cap_empty_set);
+               /* Clear the capabilities if we switch to a non-root user */
+               if (override_cred->uid)
+                       cap_clear(override_cred->cap_effective);
                else
-                       old_cap = cap_set_effective(current->cap_permitted);
+                       override_cred->cap_effective =
+                               override_cred->cap_permitted;
        }
 
+       old_cred = override_creds(override_cred);
+
        res = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path);
        if (res)
                goto out;
@@ -494,12 +488,8 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
 out_path_release:
        path_put(&path);
 out:
-       current->fsuid = old_fsuid;
-       current->fsgid = old_fsgid;
-
-       if (!issecure(SECURE_NO_SETUID_FIXUP))
-               cap_set_effective(old_cap);
-
+       revert_creds(old_cred);
+       put_cred(override_cred);
        return res;
 }
 
@@ -792,7 +782,8 @@ static inline int __get_file_write_access(struct inode *inode,
 
 static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
                                        int flags, struct file *f,
-                                       int (*open)(struct inode *, struct file *))
+                                       int (*open)(struct inode *, struct file *),
+                                       const struct cred *cred)
 {
        struct inode *inode;
        int error;
@@ -816,7 +807,7 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
        f->f_op = fops_get(inode->i_fop);
        file_move(f, &inode->i_sb->s_files);
 
-       error = security_dentry_open(f);
+       error = security_dentry_open(f, cred);
        if (error)
                goto cleanup_all;
 
@@ -891,6 +882,8 @@ cleanup_file:
 struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry,
                int (*open)(struct inode *, struct file *))
 {
+       const struct cred *cred = current_cred();
+
        if (IS_ERR(nd->intent.open.file))
                goto out;
        if (IS_ERR(dentry))
@@ -898,7 +891,7 @@ struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry
        nd->intent.open.file = __dentry_open(dget(dentry), mntget(nd->path.mnt),
                                             nd->intent.open.flags - 1,
                                             nd->intent.open.file,
-                                            open);
+                                            open, cred);
 out:
        return nd->intent.open.file;
 out_err:
@@ -917,6 +910,7 @@ EXPORT_SYMBOL_GPL(lookup_instantiate_filp);
  */
 struct file *nameidata_to_filp(struct nameidata *nd, int flags)
 {
+       const struct cred *cred = current_cred();
        struct file *filp;
 
        /* Pick up the filp from the open intent */
@@ -924,7 +918,7 @@ struct file *nameidata_to_filp(struct nameidata *nd, int flags)
        /* Has the filesystem initialised the file for us? */
        if (filp->f_path.dentry == NULL)
                filp = __dentry_open(nd->path.dentry, nd->path.mnt, flags, filp,
-                                    NULL);
+                                    NULL, cred);
        else
                path_put(&nd->path);
        return filp;
@@ -934,7 +928,8 @@ struct file *nameidata_to_filp(struct nameidata *nd, int flags)
  * dentry_open() will have done dput(dentry) and mntput(mnt) if it returns an
  * error.
  */
-struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
+struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags,
+                        const struct cred *cred)
 {
        int error;
        struct file *f;
@@ -959,7 +954,7 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
                return ERR_PTR(error);
        }
 
-       return __dentry_open(dentry, mnt, flags, f, NULL);
+       return __dentry_open(dentry, mnt, flags, f, NULL, cred);
 }
 EXPORT_SYMBOL(dentry_open);
 
index 7aea8b8..aaf797b 100644 (file)
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -899,8 +899,8 @@ static struct inode * get_pipe_inode(void)
         */
        inode->i_state = I_DIRTY;
        inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR;
-       inode->i_uid = current->fsuid;
-       inode->i_gid = current->fsgid;
+       inode->i_uid = current_fsuid();
+       inode->i_gid = current_fsgid();
        inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 
        return inode;
index aec931e..39df95a 100644 (file)
@@ -217,11 +217,11 @@ posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
                 switch(pa->e_tag) {
                         case ACL_USER_OBJ:
                                /* (May have been checked already) */
-                                if (inode->i_uid == current->fsuid)
+                               if (inode->i_uid == current_fsuid())
                                         goto check_perm;
                                 break;
                         case ACL_USER:
-                                if (pa->e_id == current->fsuid)
+                               if (pa->e_id == current_fsuid())
                                         goto mask;
                                break;
                         case ACL_GROUP_OBJ:
index 6af7fba..7e4877d 100644 (file)
@@ -159,6 +159,7 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
        struct group_info *group_info;
        int g;
        struct fdtable *fdt = NULL;
+       const struct cred *cred;
        pid_t ppid, tpid;
 
        rcu_read_lock();
@@ -170,6 +171,7 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
                if (tracer)
                        tpid = task_pid_nr_ns(tracer, ns);
        }
+       cred = get_cred((struct cred *) __task_cred(p));
        seq_printf(m,
                "State:\t%s\n"
                "Tgid:\t%d\n"
@@ -182,8 +184,8 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
                task_tgid_nr_ns(p, ns),
                pid_nr_ns(pid, ns),
                ppid, tpid,
-               p->uid, p->euid, p->suid, p->fsuid,
-               p->gid, p->egid, p->sgid, p->fsgid);
+               cred->uid, cred->euid, cred->suid, cred->fsuid,
+               cred->gid, cred->egid, cred->sgid, cred->fsgid);
 
        task_lock(p);
        if (p->files)
@@ -194,13 +196,12 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
                fdt ? fdt->max_fds : 0);
        rcu_read_unlock();
 
-       group_info = p->group_info;
-       get_group_info(group_info);
+       group_info = cred->group_info;
        task_unlock(p);
 
        for (g = 0; g < min(group_info->ngroups, NGROUPS_SMALL); g++)
                seq_printf(m, "%d ", GROUP_AT(group_info, g));
-       put_group_info(group_info);
+       put_cred(cred);
 
        seq_printf(m, "\n");
 }
@@ -262,7 +263,7 @@ static inline void task_sig(struct seq_file *m, struct task_struct *p)
                blocked = p->blocked;
                collect_sigign_sigcatch(p, &ignored, &caught);
                num_threads = atomic_read(&p->signal->count);
-               qsize = atomic_read(&p->user->sigpending);
+               qsize = atomic_read(&__task_cred(p)->user->sigpending);
                qlim = p->signal->rlim[RLIMIT_SIGPENDING].rlim_cur;
                unlock_task_sighand(p, &flags);
        }
@@ -293,10 +294,21 @@ static void render_cap_t(struct seq_file *m, const char *header,
 
 static inline void task_cap(struct seq_file *m, struct task_struct *p)
 {
-       render_cap_t(m, "CapInh:\t", &p->cap_inheritable);
-       render_cap_t(m, "CapPrm:\t", &p->cap_permitted);
-       render_cap_t(m, "CapEff:\t", &p->cap_effective);
-       render_cap_t(m, "CapBnd:\t", &p->cap_bset);
+       const struct cred *cred;
+       kernel_cap_t cap_inheritable, cap_permitted, cap_effective, cap_bset;
+
+       rcu_read_lock();
+       cred = __task_cred(p);
+       cap_inheritable = cred->cap_inheritable;
+       cap_permitted   = cred->cap_permitted;
+       cap_effective   = cred->cap_effective;
+       cap_bset        = cred->cap_bset;
+       rcu_read_unlock();
+
+       render_cap_t(m, "CapInh:\t", &cap_inheritable);
+       render_cap_t(m, "CapPrm:\t", &cap_permitted);
+       render_cap_t(m, "CapEff:\t", &cap_effective);
+       render_cap_t(m, "CapBnd:\t", &cap_bset);
 }
 
 static inline void task_context_switch_counts(struct seq_file *m,
index d467760..0a8a5f8 100644 (file)
@@ -1406,6 +1406,7 @@ static struct inode *proc_pid_make_inode(struct super_block * sb, struct task_st
 {
        struct inode * inode;
        struct proc_inode *ei;
+       const struct cred *cred;
 
        /* We need a new inode */
 
@@ -1428,8 +1429,11 @@ static struct inode *proc_pid_make_inode(struct super_block * sb, struct task_st
        inode->i_uid = 0;
        inode->i_gid = 0;
        if (task_dumpable(task)) {
-               inode->i_uid = task->euid;
-               inode->i_gid = task->egid;
+               rcu_read_lock();
+               cred = __task_cred(task);
+               inode->i_uid = cred->euid;
+               inode->i_gid = cred->egid;
+               rcu_read_unlock();
        }
        security_task_to_inode(task, inode);
 
@@ -1445,6 +1449,8 @@ static int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat
 {
        struct inode *inode = dentry->d_inode;
        struct task_struct *task;
+       const struct cred *cred;
+
        generic_fillattr(inode, stat);
 
        rcu_read_lock();
@@ -1454,8 +1460,9 @@ static int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat
        if (task) {
                if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) ||
                    task_dumpable(task)) {
-                       stat->uid = task->euid;
-                       stat->gid = task->egid;
+                       cred = __task_cred(task);
+                       stat->uid = cred->euid;
+                       stat->gid = cred->egid;
                }
        }
        rcu_read_unlock();
@@ -1483,11 +1490,16 @@ static int pid_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
        struct inode *inode = dentry->d_inode;
        struct task_struct *task = get_proc_task(inode);
+       const struct cred *cred;
+
        if (task) {
                if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) ||
                    task_dumpable(task)) {
-                       inode->i_uid = task->euid;
-                       inode->i_gid = task->egid;
+                       rcu_read_lock();
+                       cred = __task_cred(task);
+                       inode->i_uid = cred->euid;
+                       inode->i_gid = cred->egid;
+                       rcu_read_unlock();
                } else {
                        inode->i_uid = 0;
                        inode->i_gid = 0;
@@ -1649,6 +1661,7 @@ static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd)
        struct task_struct *task = get_proc_task(inode);
        int fd = proc_fd(inode);
        struct files_struct *files;
+       const struct cred *cred;
 
        if (task) {
                files = get_files_struct(task);
@@ -1658,8 +1671,11 @@ static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd)
                                rcu_read_unlock();
                                put_files_struct(files);
                                if (task_dumpable(task)) {
-                                       inode->i_uid = task->euid;
-                                       inode->i_gid = task->egid;
+                                       rcu_read_lock();
+                                       cred = __task_cred(task);
+                                       inode->i_uid = cred->euid;
+                                       inode->i_gid = cred->egid;
+                                       rcu_read_unlock();
                                } else {
                                        inode->i_uid = 0;
                                        inode->i_gid = 0;
index 7f4386e..b7fe44e 100644 (file)
@@ -79,7 +79,7 @@ static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, qid
 
        /* Check privileges */
        if (cmd == Q_GETQUOTA) {
-               if (((type == USRQUOTA && current->euid != id) ||
+               if (((type == USRQUOTA && current_euid() != id) ||
                     (type == GRPQUOTA && !in_egroup_p(id))) &&
                    !capable(CAP_SYS_ADMIN))
                        return -EPERM;
@@ -130,7 +130,7 @@ static int xqm_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t i
 
        /* Check privileges */
        if (cmd == Q_XGETQUOTA) {
-               if (((type == XQM_USRQUOTA && current->euid != id) ||
+               if (((type == XQM_USRQUOTA && current_euid() != id) ||
                     (type == XQM_GRPQUOTA && !in_egroup_p(id))) &&
                     !capable(CAP_SYS_ADMIN))
                        return -EPERM;
index f031d1c..a83a351 100644 (file)
@@ -55,8 +55,8 @@ struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev)
 
        if (inode) {
                inode->i_mode = mode;
-               inode->i_uid = current->fsuid;
-               inode->i_gid = current->fsgid;
+               inode->i_uid = current_fsuid();
+               inode->i_gid = current_fsgid();
                inode->i_blocks = 0;
                inode->i_mapping->a_ops = &ramfs_aops;
                inode->i_mapping->backing_dev_info = &ramfs_backing_dev_info;
index f89ebb9..4f322e5 100644 (file)
@@ -573,7 +573,7 @@ static int new_inode_init(struct inode *inode, struct inode *dir, int mode)
        /* the quota init calls have to know who to charge the quota to, so
         ** we have to set uid and gid here
         */
-       inode->i_uid = current->fsuid;
+       inode->i_uid = current_fsuid();
        inode->i_mode = mode;
        /* Make inode invalid - just in case we are going to drop it before
         * the initialization happens */
@@ -584,7 +584,7 @@ static int new_inode_init(struct inode *inode, struct inode *dir, int mode)
                if (S_ISDIR(mode))
                        inode->i_mode |= S_ISGID;
        } else {
-               inode->i_gid = current->fsgid;
+               inode->i_gid = current_fsgid();
        }
        DQUOT_INIT(inode);
        return 0;
index 48da4fa..e7ddd03 100644 (file)
@@ -667,8 +667,7 @@ smb_make_node(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
 
        attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID;
        attr.ia_mode = mode;
-       attr.ia_uid = current->euid;
-       attr.ia_gid = current->egid;
+       current_euid_egid(&attr.ia_uid, &attr.ia_gid);
 
        if (!new_valid_dev(dev))
                return -EINVAL;
index 3528f40..fc27fbf 100644 (file)
@@ -586,7 +586,7 @@ static int smb_fill_super(struct super_block *sb, void *raw_data, int silent)
                if (parse_options(mnt, raw_data))
                        goto out_bad_option;
        }
-       mnt->mounted_uid = current->uid;
+       mnt->mounted_uid = current_uid();
        smb_setcodepage(server, &mnt->codepage);
 
        /*
index ee536e8..9468168 100644 (file)
@@ -864,7 +864,7 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
                goto out;
 
        error = -EACCES;
-       if (current->uid != server->mnt->mounted_uid && 
+       if (current_uid() != server->mnt->mounted_uid &&
            !capable(CAP_SYS_ADMIN))
                goto out;
 
index 400a760..ddba069 100644 (file)
@@ -914,7 +914,7 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
                goto out_free_secdata;
        BUG_ON(!mnt->mnt_sb);
 
-       error = security_sb_kern_mount(mnt->mnt_sb, secdata);
+       error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);
        if (error)
                goto out_sb;
 
index 115ab0d..241e976 100644 (file)
@@ -165,9 +165,9 @@ struct inode * sysv_new_inode(const struct inode * dir, mode_t mode)
                if (S_ISDIR(mode))
                        mode |= S_ISGID;
        } else
-               inode->i_gid = current->fsgid;
+               inode->i_gid = current_fsgid();
 
-       inode->i_uid = current->fsuid;
+       inode->i_uid = current_fsuid();
        inode->i_ino = fs16_to_cpu(sbi, ino);
        inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
        inode->i_blocks = 0;
index 1a4973e..4a18f08 100644 (file)
@@ -363,7 +363,7 @@ long long ubifs_calc_available(const struct ubifs_info *c, int min_idx_lebs)
  */
 static int can_use_rp(struct ubifs_info *c)
 {
-       if (current->fsuid == c->rp_uid || capable(CAP_SYS_RESOURCE) ||
+       if (current_fsuid() == c->rp_uid || capable(CAP_SYS_RESOURCE) ||
            (c->rp_gid != 0 && in_group_p(c->rp_gid)))
                return 1;
        return 0;
index 0422c98..f448ab1 100644 (file)
@@ -104,13 +104,13 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,
         */
        inode->i_flags |= (S_NOCMTIME);
 
-       inode->i_uid = current->fsuid;
+       inode->i_uid = current_fsuid();
        if (dir->i_mode & S_ISGID) {
                inode->i_gid = dir->i_gid;
                if (S_ISDIR(mode))
                        mode |= S_ISGID;
        } else
-               inode->i_gid = current->fsgid;
+               inode->i_gid = current_fsgid();
        inode->i_mode = mode;
        inode->i_mtime = inode->i_atime = inode->i_ctime =
                         ubifs_current_time(inode);
index a4f2b3c..31fc842 100644 (file)
@@ -126,13 +126,13 @@ struct inode *udf_new_inode(struct inode *dir, int mode, int *err)
        }
        mutex_unlock(&sbi->s_alloc_mutex);
        inode->i_mode = mode;
-       inode->i_uid = current->fsuid;
+       inode->i_uid = current_fsuid();
        if (dir->i_mode & S_ISGID) {
                inode->i_gid = dir->i_gid;
                if (S_ISDIR(mode))
                        mode |= S_ISGID;
        } else {
-               inode->i_gid = current->fsgid;
+               inode->i_gid = current_fsgid();
        }
 
        iinfo->i_location.logicalBlockNum = block;
index 082409c..f84bfaa 100644 (file)
@@ -604,7 +604,7 @@ static int udf_mknod(struct inode *dir, struct dentry *dentry, int mode,
                goto out;
 
        iinfo = UDF_I(inode);
-       inode->i_uid = current->fsuid;
+       inode->i_uid = current_fsuid();
        init_special_inode(inode, mode, rdev);
        fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);
        if (!fi) {
index ac181f6..6f5dcf0 100644 (file)
@@ -304,13 +304,13 @@ cg_found:
 
        inode->i_ino = cg * uspi->s_ipg + bit;
        inode->i_mode = mode;
-       inode->i_uid = current->fsuid;
+       inode->i_uid = current_fsuid();
        if (dir->i_mode & S_ISGID) {
                inode->i_gid = dir->i_gid;
                if (S_ISDIR(mode))
                        inode->i_mode |= S_ISGID;
        } else
-               inode->i_gid = current->fsgid;
+               inode->i_gid = current_fsgid();
 
        inode->i_blocks = 0;
        inode->i_generation = 0;
index 652721c..8c022cd 100644 (file)
 /*
  * Credentials
  */
-typedef struct cred {
-       /* EMPTY */
-} cred_t;
+typedef const struct cred cred_t;
 
-extern struct cred *sys_cred;
+extern cred_t *sys_cred;
 
 /* this is a hack.. (assumes sys_cred is the only cred_t in the system) */
 static inline int capable_cred(cred_t *cr, int cid)
index 2770b00..6eda8a3 100644 (file)
@@ -19,6 +19,6 @@
 #define __XFS_GLOBALS_H__
 
 extern uint64_t        xfs_panic_mask;         /* set to cause more panics */
-extern struct cred *sys_cred;
+extern cred_t *sys_cred;
 
 #endif /* __XFS_GLOBALS_H__ */
index d3438c7..281cbd5 100644 (file)
@@ -256,6 +256,7 @@ xfs_open_by_handle(
        struct file             *parfilp,
        struct inode            *parinode)
 {
+       const struct cred       *cred = current_cred();
        int                     error;
        int                     new_fd;
        int                     permflag;
@@ -321,7 +322,7 @@ xfs_open_by_handle(
        mntget(parfilp->f_path.mnt);
 
        /* Create file pointer. */
-       filp = dentry_open(dentry, parfilp->f_path.mnt, hreq.oflags);
+       filp = dentry_open(dentry, parfilp->f_path.mnt, hreq.oflags, cred);
        if (IS_ERR(filp)) {
                put_unused_fd(new_fd);
                return -XFS_ERROR(-PTR_ERR(filp));
@@ -1007,7 +1008,7 @@ xfs_ioctl_setattr(
         * to the file owner ID, except in cases where the
         * CAP_FSETID capability is applicable.
         */
-       if (current->fsuid != ip->i_d.di_uid && !capable(CAP_FOWNER)) {
+       if (current_fsuid() != ip->i_d.di_uid && !capable(CAP_FOWNER)) {
                code = XFS_ERROR(EPERM);
                goto error_return;
        }
index b2f639a..91d6933 100644 (file)
@@ -366,7 +366,7 @@ xfs_acl_allow_set(
                return ENOTDIR;
        if (vp->i_sb->s_flags & MS_RDONLY)
                return EROFS;
-       if (XFS_I(vp)->i_d.di_uid != current->fsuid && !capable(CAP_FOWNER))
+       if (XFS_I(vp)->i_d.di_uid != current_fsuid() && !capable(CAP_FOWNER))
                return EPERM;
        return 0;
 }
@@ -413,13 +413,13 @@ xfs_acl_access(
                switch (fap->acl_entry[i].ae_tag) {
                case ACL_USER_OBJ:
                        seen_userobj = 1;
-                       if (fuid != current->fsuid)
+                       if (fuid != current_fsuid())
                                continue;
                        matched.ae_tag = ACL_USER_OBJ;
                        matched.ae_perm = allows;
                        break;
                case ACL_USER:
-                       if (fap->acl_entry[i].ae_id != current->fsuid)
+                       if (fap->acl_entry[i].ae_id != current_fsuid())
                                continue;
                        matched.ae_tag = ACL_USER;
                        matched.ae_perm = allows;
index 1420c49..6be310d 100644 (file)
@@ -497,7 +497,7 @@ int         xfs_iread(struct xfs_mount *, struct xfs_trans *, xfs_ino_t,
                          xfs_inode_t **, xfs_daddr_t, uint);
 int            xfs_iread_extents(struct xfs_trans *, xfs_inode_t *, int);
 int            xfs_ialloc(struct xfs_trans *, xfs_inode_t *, mode_t,
-                          xfs_nlink_t, xfs_dev_t, struct cred *, xfs_prid_t,
+                          xfs_nlink_t, xfs_dev_t, cred_t *, xfs_prid_t,
                           int, struct xfs_buf **, boolean_t *, xfs_inode_t **);
 void           xfs_dinode_from_disk(struct xfs_icdinode *,
                                     struct xfs_dinode_core *);
index e932a96..7b0c2ab 100644 (file)
@@ -16,7 +16,7 @@ struct xfs_iomap;
 
 int xfs_open(struct xfs_inode *ip);
 int xfs_setattr(struct xfs_inode *ip, struct iattr *vap, int flags,
-               struct cred *credp);
+               cred_t *credp);
 #define        XFS_ATTR_DMI            0x01    /* invocation from a DMI function */
 #define        XFS_ATTR_NONBLOCK       0x02    /* return EAGAIN if operation would block */
 #define XFS_ATTR_NOLOCK                0x04    /* Don't grab any conflicting locks */
@@ -28,24 +28,24 @@ int xfs_inactive(struct xfs_inode *ip);
 int xfs_lookup(struct xfs_inode *dp, struct xfs_name *name,
                struct xfs_inode **ipp, struct xfs_name *ci_name);
 int xfs_create(struct xfs_inode *dp, struct xfs_name *name, mode_t mode,
-               xfs_dev_t rdev, struct xfs_inode **ipp, struct cred *credp);
+               xfs_dev_t rdev, struct xfs_inode **ipp, cred_t *credp);
 int xfs_remove(struct xfs_inode *dp, struct xfs_name *name,
                struct xfs_inode *ip);
 int xfs_link(struct xfs_inode *tdp, struct xfs_inode *sip,
                struct xfs_name *target_name);
 int xfs_mkdir(struct xfs_inode *dp, struct xfs_name *dir_name,
-               mode_t mode, struct xfs_inode **ipp, struct cred *credp);
+               mode_t mode, struct xfs_inode **ipp, cred_t *credp);
 int xfs_readdir(struct xfs_inode       *dp, void *dirent, size_t bufsize,
                       xfs_off_t *offset, filldir_t filldir);
 int xfs_symlink(struct xfs_inode *dp, struct xfs_name *link_name,
                const char *target_path, mode_t mode, struct xfs_inode **ipp,
-               struct cred *credp);
+               cred_t *credp);
 int xfs_inode_flush(struct xfs_inode *ip, int flags);
 int xfs_set_dmattrs(struct xfs_inode *ip, u_int evmask, u_int16_t state);
 int xfs_reclaim(struct xfs_inode *ip);
 int xfs_change_file_space(struct xfs_inode *ip, int cmd,
                xfs_flock64_t *bf, xfs_off_t offset,
-               struct cred *credp, int attr_flags);
+               cred_t *credp, int      attr_flags);
 int xfs_rename(struct xfs_inode *src_dp, struct xfs_name *src_name,
                struct xfs_inode *src_ip, struct xfs_inode *target_dp,
                struct xfs_name *target_name, struct xfs_inode *target_ip);
index 40008d6..656a4c6 100644 (file)
@@ -23,10 +23,10 @@ struct crypto_aes_ctx {
        u32 key_dec[AES_MAX_KEYLENGTH_U32];
 };
 
-extern u32 crypto_ft_tab[4][256];
-extern u32 crypto_fl_tab[4][256];
-extern u32 crypto_it_tab[4][256];
-extern u32 crypto_il_tab[4][256];
+extern const u32 crypto_ft_tab[4][256];
+extern const u32 crypto_fl_tab[4][256];
+extern const u32 crypto_it_tab[4][256];
+extern const u32 crypto_il_tab[4][256];
 
 int crypto_aes_set_key(struct crypto_tfm *tfm, const u8 *in_key,
                unsigned int key_len);
index 60d06e7..0105454 100644 (file)
@@ -22,9 +22,18 @@ struct seq_file;
 
 struct crypto_type {
        unsigned int (*ctxsize)(struct crypto_alg *alg, u32 type, u32 mask);
+       unsigned int (*extsize)(struct crypto_alg *alg,
+                               const struct crypto_type *frontend);
        int (*init)(struct crypto_tfm *tfm, u32 type, u32 mask);
-       void (*exit)(struct crypto_tfm *tfm);
+       int (*init_tfm)(struct crypto_tfm *tfm,
+                       const struct crypto_type *frontend);
        void (*show)(struct seq_file *m, struct crypto_alg *alg);
+       struct crypto_alg *(*lookup)(const char *name, u32 type, u32 mask);
+
+       unsigned int type;
+       unsigned int maskclear;
+       unsigned int maskset;
+       unsigned int tfmsize;
 };
 
 struct crypto_instance {
@@ -239,6 +248,11 @@ static inline struct crypto_hash *crypto_spawn_hash(struct crypto_spawn *spawn)
        return __crypto_hash_cast(crypto_spawn_tfm(spawn, type, mask));
 }
 
+static inline void *crypto_hash_ctx(struct crypto_hash *tfm)
+{
+       return crypto_tfm_ctx(&tfm->base);
+}
+
 static inline void *crypto_hash_ctx_aligned(struct crypto_hash *tfm)
 {
        return crypto_tfm_ctx_aligned(&tfm->base);
index ee48ef8..cd16d6e 100644 (file)
 
 #include <linux/crypto.h>
 
+struct shash_desc {
+       struct crypto_shash *tfm;
+       u32 flags;
+
+       void *__ctx[] CRYPTO_MINALIGN_ATTR;
+};
+
+struct shash_alg {
+       int (*init)(struct shash_desc *desc);
+       int (*reinit)(struct shash_desc *desc);
+       int (*update)(struct shash_desc *desc, const u8 *data,
+                     unsigned int len);
+       int (*final)(struct shash_desc *desc, u8 *out);
+       int (*finup)(struct shash_desc *desc, const u8 *data,
+                    unsigned int len, u8 *out);
+       int (*digest)(struct shash_desc *desc, const u8 *data,
+                     unsigned int len, u8 *out);
+       int (*setkey)(struct crypto_shash *tfm, const u8 *key,
+                     unsigned int keylen);
+
+       unsigned int descsize;
+       unsigned int digestsize;
+
+       struct crypto_alg base;
+};
+
 struct crypto_ahash {
        struct crypto_tfm base;
 };
 
+struct crypto_shash {
+       struct crypto_tfm base;
+};
+
 static inline struct crypto_ahash *__crypto_ahash_cast(struct crypto_tfm *tfm)
 {
        return (struct crypto_ahash *)tfm;
@@ -87,6 +117,11 @@ static inline unsigned int crypto_ahash_reqsize(struct crypto_ahash *tfm)
        return crypto_ahash_crt(tfm)->reqsize;
 }
 
+static inline void *ahash_request_ctx(struct ahash_request *req)
+{
+       return req->__ctx;
+}
+
 static inline int crypto_ahash_setkey(struct crypto_ahash *tfm,
                                      const u8 *key, unsigned int keylen)
 {
@@ -101,6 +136,14 @@ static inline int crypto_ahash_digest(struct ahash_request *req)
        return crt->digest(req);
 }
 
+static inline void crypto_ahash_export(struct ahash_request *req, u8 *out)
+{
+       memcpy(out, ahash_request_ctx(req),
+              crypto_ahash_reqsize(crypto_ahash_reqtfm(req)));
+}
+
+int crypto_ahash_import(struct ahash_request *req, const u8 *in);
+
 static inline int crypto_ahash_init(struct ahash_request *req)
 {
        struct ahash_tfm *crt = crypto_ahash_crt(crypto_ahash_reqtfm(req));
@@ -169,4 +212,86 @@ static inline void ahash_request_set_crypt(struct ahash_request *req,
        req->result = result;
 }
 
+struct crypto_shash *crypto_alloc_shash(const char *alg_name, u32 type,
+                                       u32 mask);
+
+static inline struct crypto_tfm *crypto_shash_tfm(struct crypto_shash *tfm)
+{
+       return &tfm->base;
+}
+
+static inline void crypto_free_shash(struct crypto_shash *tfm)
+{
+       crypto_free_tfm(crypto_shash_tfm(tfm));
+}
+
+static inline unsigned int crypto_shash_alignmask(
+       struct crypto_shash *tfm)
+{
+       return crypto_tfm_alg_alignmask(crypto_shash_tfm(tfm));
+}
+
+static inline struct shash_alg *__crypto_shash_alg(struct crypto_alg *alg)
+{
+       return container_of(alg, struct shash_alg, base);
+}
+
+static inline struct shash_alg *crypto_shash_alg(struct crypto_shash *tfm)
+{
+       return __crypto_shash_alg(crypto_shash_tfm(tfm)->__crt_alg);
+}
+
+static inline unsigned int crypto_shash_digestsize(struct crypto_shash *tfm)
+{
+       return crypto_shash_alg(tfm)->digestsize;
+}
+
+static inline u32 crypto_shash_get_flags(struct crypto_shash *tfm)
+{
+       return crypto_tfm_get_flags(crypto_shash_tfm(tfm));
+}
+
+static inline void crypto_shash_set_flags(struct crypto_shash *tfm, u32 flags)
+{
+       crypto_tfm_set_flags(crypto_shash_tfm(tfm), flags);
+}
+
+static inline void crypto_shash_clear_flags(struct crypto_shash *tfm, u32 flags)
+{
+       crypto_tfm_clear_flags(crypto_shash_tfm(tfm), flags);
+}
+
+static inline unsigned int crypto_shash_descsize(struct crypto_shash *tfm)
+{
+       return crypto_shash_alg(tfm)->descsize;
+}
+
+static inline void *shash_desc_ctx(struct shash_desc *desc)
+{
+       return desc->__ctx;
+}
+
+int crypto_shash_setkey(struct crypto_shash *tfm, const u8 *key,
+                       unsigned int keylen);
+int crypto_shash_digest(struct shash_desc *desc, const u8 *data,
+                       unsigned int len, u8 *out);
+
+static inline void crypto_shash_export(struct shash_desc *desc, u8 *out)
+{
+       memcpy(out, shash_desc_ctx(desc), crypto_shash_descsize(desc->tfm));
+}
+
+int crypto_shash_import(struct shash_desc *desc, const u8 *in);
+
+static inline int crypto_shash_init(struct shash_desc *desc)
+{
+       return crypto_shash_alg(desc->tfm)->init(desc);
+}
+
+int crypto_shash_update(struct shash_desc *desc, const u8 *data,
+                       unsigned int len);
+int crypto_shash_final(struct shash_desc *desc, u8 *out);
+int crypto_shash_finup(struct shash_desc *desc, const u8 *data,
+                      unsigned int len, u8 *out);
+
 #endif /* _CRYPTO_HASH_H */
index 917ae57..82b7056 100644 (file)
@@ -39,6 +39,12 @@ extern const struct crypto_type crypto_ahash_type;
 int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err);
 int crypto_hash_walk_first(struct ahash_request *req,
                           struct crypto_hash_walk *walk);
+int crypto_hash_walk_first_compat(struct hash_desc *hdesc,
+                                 struct crypto_hash_walk *walk,
+                                 struct scatterlist *sg, unsigned int len);
+
+int crypto_register_shash(struct shash_alg *alg);
+int crypto_unregister_shash(struct shash_alg *alg);
 
 static inline void *crypto_ahash_ctx(struct crypto_ahash *tfm)
 {
@@ -63,16 +69,16 @@ static inline struct ahash_request *ahash_dequeue_request(
        return ahash_request_cast(crypto_dequeue_request(queue));
 }
 
-static inline void *ahash_request_ctx(struct ahash_request *req)
-{
-       return req->__ctx;
-}
-
 static inline int ahash_tfm_in_queue(struct crypto_queue *queue,
                                          struct crypto_ahash *tfm)
 {
        return crypto_tfm_in_queue(queue, crypto_ahash_tfm(tfm));
 }
 
+static inline void *crypto_shash_ctx(struct crypto_shash *tfm)
+{
+       return crypto_tfm_ctx(&tfm->base);
+}
+
 #endif /* _CRYPTO_INTERNAL_HASH_H */
 
diff --git a/include/keys/keyring-type.h b/include/keys/keyring-type.h
new file mode 100644 (file)
index 0000000..843f872
--- /dev/null
@@ -0,0 +1,31 @@
+/* Keyring key type
+ *
+ * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _KEYS_KEYRING_TYPE_H
+#define _KEYS_KEYRING_TYPE_H
+
+#include <linux/key.h>
+#include <linux/rcupdate.h>
+
+/*
+ * the keyring payload contains a list of the keys to which the keyring is
+ * subscribed
+ */
+struct keyring_list {
+       struct rcu_head rcu;            /* RCU deletion hook */
+       unsigned short  maxkeys;        /* max keys this list can hold */
+       unsigned short  nkeys;          /* number of keys currently held */
+       unsigned short  delkey;         /* key to be unlinked by RCU */
+       struct key      *keys[0];
+};
+
+
+#endif /* _KEYS_KEYRING_TYPE_H */
index 8f0672d..26c4f6f 100644 (file)
@@ -99,6 +99,8 @@
 #define AUDIT_OBJ_PID          1318    /* ptrace target */
 #define AUDIT_TTY              1319    /* Input on an administrative TTY */
 #define AUDIT_EOE              1320    /* End of multi-record event */
+#define AUDIT_BPRM_FCAPS       1321    /* Information about fcaps increasing perms */
+#define AUDIT_CAPSET           1322    /* Record showing argument to sys_capset */
 
 #define AUDIT_AVC              1400    /* SE Linux avc denial or grant */
 #define AUDIT_SELINUX_ERR      1401    /* Internal SE Linux Errors */
@@ -453,6 +455,10 @@ extern int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_pr
 extern int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout);
 extern int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification);
 extern int __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat);
+extern int __audit_log_bprm_fcaps(struct linux_binprm *bprm,
+                                 const struct cred *new,
+                                 const struct cred *old);
+extern int __audit_log_capset(pid_t pid, const struct cred *new, const struct cred *old);
 
 static inline int audit_ipc_obj(struct kern_ipc_perm *ipcp)
 {
@@ -502,6 +508,24 @@ static inline int audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat)
                return __audit_mq_getsetattr(mqdes, mqstat);
        return 0;
 }
+
+static inline int audit_log_bprm_fcaps(struct linux_binprm *bprm,
+                                      const struct cred *new,
+                                      const struct cred *old)
+{
+       if (unlikely(!audit_dummy_context()))
+               return __audit_log_bprm_fcaps(bprm, new, old);
+       return 0;
+}
+
+static inline int audit_log_capset(pid_t pid, const struct cred *new,
+                                  const struct cred *old)
+{
+       if (unlikely(!audit_dummy_context()))
+               return __audit_log_capset(pid, new, old);
+       return 0;
+}
+
 extern int audit_n_rules;
 extern int audit_signals;
 #else
@@ -534,6 +558,8 @@ extern int audit_signals;
 #define audit_mq_timedreceive(d,l,p,t) ({ 0; })
 #define audit_mq_notify(d,n) ({ 0; })
 #define audit_mq_getsetattr(d,s) ({ 0; })
+#define audit_log_bprm_fcaps(b, ncr, ocr) ({ 0; })
+#define audit_log_capset(pid, ncr, ocr) ({ 0; })
 #define audit_ptrace(t) ((void)0)
 #define audit_n_rules 0
 #define audit_signals 0
index 7394b5b..6cbfbe2 100644 (file)
@@ -35,16 +35,20 @@ struct linux_binprm{
        struct mm_struct *mm;
        unsigned long p; /* current top of mem */
        unsigned int sh_bang:1,
-                    misc_bang:1;
+               misc_bang:1,
+               cred_prepared:1,/* true if creds already prepared (multiple
+                                * preps happen for interpreters) */
+               cap_effective:1;/* true if has elevated effective capabilities,
+                                * false if not; except for init which inherits
+                                * its parent's caps anyway */
 #ifdef __alpha__
        unsigned int taso:1;
 #endif
        unsigned int recursion_depth;
        struct file * file;
-       int e_uid, e_gid;
-       kernel_cap_t cap_post_exec_permitted;
-       bool cap_effective;
-       void *security;
+       struct cred *cred;      /* new credentials */
+       int unsafe;             /* how unsafe this exec is (mask of LSM_UNSAFE_*) */
+       unsigned int per_clear; /* bits to clear in current->personality */
        int argc, envc;
        char * filename;        /* Name of binary as seen by procps */
        char * interp;          /* Name of the binary really executed. Most
@@ -101,7 +105,7 @@ extern int setup_arg_pages(struct linux_binprm * bprm,
                           int executable_stack);
 extern int bprm_mm_init(struct linux_binprm *bprm);
 extern int copy_strings_kernel(int argc,char ** argv,struct linux_binprm *bprm);
-extern void compute_creds(struct linux_binprm *binprm);
+extern void install_exec_creds(struct linux_binprm *bprm);
 extern int do_coredump(long signr, int exit_code, struct pt_regs * regs);
 extern int set_binfmt(struct linux_binfmt *new);
 extern void free_bprm(struct linux_binprm *);
index 9d1fe30..e22f48c 100644 (file)
@@ -53,6 +53,7 @@ typedef struct __user_cap_data_struct {
 #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX
 
 #define VFS_CAP_REVISION_MASK  0xFF000000
+#define VFS_CAP_REVISION_SHIFT 24
 #define VFS_CAP_FLAGS_MASK     ~VFS_CAP_REVISION_MASK
 #define VFS_CAP_FLAGS_EFFECTIVE        0x000001
 
@@ -68,6 +69,9 @@ typedef struct __user_cap_data_struct {
 #define VFS_CAP_U32             VFS_CAP_U32_2
 #define VFS_CAP_REVISION       VFS_CAP_REVISION_2
 
+#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
+extern int file_caps_enabled;
+#endif
 
 struct vfs_cap_data {
        __le32 magic_etc;            /* Little endian */
@@ -96,6 +100,13 @@ typedef struct kernel_cap_struct {
        __u32 cap[_KERNEL_CAPABILITY_U32S];
 } kernel_cap_t;
 
+/* exact same as vfs_cap_data but in cpu endian and always filled completely */
+struct cpu_vfs_cap_data {
+       __u32 magic_etc;
+       kernel_cap_t permitted;
+       kernel_cap_t inheritable;
+};
+
 #define _USER_CAP_HEADER_SIZE  (sizeof(struct __user_cap_header_struct))
 #define _KERNEL_CAP_T_SIZE     (sizeof(kernel_cap_t))
 
@@ -454,6 +465,13 @@ static inline int cap_isclear(const kernel_cap_t a)
        return 1;
 }
 
+/*
+ * Check if "a" is a subset of "set".
+ * return 1 if ALL of the capabilities in "a" are also in "set"
+ *     cap_issubset(0101, 1111) will return 1
+ * return 0 if ANY of the capabilities in "a" are not in "set"
+ *     cap_issubset(1111, 0101) will return 0
+ */
 static inline int cap_issubset(const kernel_cap_t a, const kernel_cap_t set)
 {
        kernel_cap_t dest;
@@ -501,8 +519,6 @@ extern const kernel_cap_t __cap_empty_set;
 extern const kernel_cap_t __cap_full_set;
 extern const kernel_cap_t __cap_init_eff_set;
 
-kernel_cap_t cap_set_effective(const kernel_cap_t pE_new);
-
 /**
  * has_capability - Determine if a task has a superior capability available
  * @t: The task in question
@@ -514,9 +530,14 @@ kernel_cap_t cap_set_effective(const kernel_cap_t pE_new);
  * Note that this does not set PF_SUPERPRIV on the task.
  */
 #define has_capability(t, cap) (security_capable((t), (cap)) == 0)
+#define has_capability_noaudit(t, cap) (security_capable_noaudit((t), (cap)) == 0)
 
 extern int capable(int cap);
 
+/* audit system wants to get cap info from files as well */
+struct dentry;
+extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps);
+
 #endif /* __KERNEL__ */
 
 #endif /* !_LINUX_CAPABILITY_H */
index 508f512..bd8b44d 100644 (file)
@@ -3,9 +3,9 @@
 
 #include <linux/types.h>
 
-extern u32 crc32c_le(u32 crc, unsigned char const *address, size_t length);
-extern u32 crc32c_be(u32 crc, unsigned char const *address, size_t length);
+extern u32 crc32c(u32 crc, const void *address, unsigned int length);
 
-#define crc32c(seed, data, length)  crc32c_le(seed, (unsigned char const *)data, length)
+/* This macro exists for backwards-compatibility. */
+#define crc32c_le crc32c
 
 #endif /* _LINUX_CRC32C_H */
index b69222c..3282ee4 100644 (file)
@@ -1,4 +1,4 @@
-/* Credentials management
+/* Credentials management - see Documentation/credentials.txt
  *
  * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
 #ifndef _LINUX_CRED_H
 #define _LINUX_CRED_H
 
-#define get_current_user()     (get_uid(current->user))
+#include <linux/capability.h>
+#include <linux/key.h>
+#include <asm/atomic.h>
 
-#define task_uid(task)         ((task)->uid)
-#define task_gid(task)         ((task)->gid)
-#define task_euid(task)                ((task)->euid)
-#define task_egid(task)                ((task)->egid)
+struct user_struct;
+struct cred;
+struct inode;
 
-#define current_uid()          (current->uid)
-#define current_gid()          (current->gid)
-#define current_euid()         (current->euid)
-#define current_egid()         (current->egid)
-#define current_suid()         (current->suid)
-#define current_sgid()         (current->sgid)
-#define current_fsuid()                (current->fsuid)
-#define current_fsgid()                (current->fsgid)
-#define current_cap()          (current->cap_effective)
+/*
+ * COW Supplementary groups list
+ */
+#define NGROUPS_SMALL          32
+#define NGROUPS_PER_BLOCK      ((unsigned int)(PAGE_SIZE / sizeof(gid_t)))
+
+struct group_info {
+       atomic_t        usage;
+       int             ngroups;
+       int             nblocks;
+       gid_t           small_block[NGROUPS_SMALL];
+       gid_t           *blocks[0];
+};
+
+/**
+ * get_group_info - Get a reference to a group info structure
+ * @group_info: The group info to reference
+ *
+ * This gets a reference to a set of supplementary groups.
+ *
+ * If the caller is accessing a task's credentials, they must hold the RCU read
+ * lock when reading.
+ */
+static inline struct group_info *get_group_info(struct group_info *gi)
+{
+       atomic_inc(&gi->usage);
+       return gi;
+}
+
+/**
+ * put_group_info - Release a reference to a group info structure
+ * @group_info: The group info to release
+ */
+#define put_group_info(group_info)                     \
+do {                                                   \
+       if (atomic_dec_and_test(&(group_info)->usage))  \
+               groups_free(group_info);                \
+} while (0)
+
+extern struct group_info *groups_alloc(int);
+extern struct group_info init_groups;
+extern void groups_free(struct group_info *);
+extern int set_current_groups(struct group_info *);
+extern int set_groups(struct cred *, struct group_info *);
+extern int groups_search(const struct group_info *, gid_t);
+
+/* access the groups "array" with this macro */
+#define GROUP_AT(gi, i) \
+       ((gi)->blocks[(i) / NGROUPS_PER_BLOCK][(i) % NGROUPS_PER_BLOCK])
+
+extern int in_group_p(gid_t);
+extern int in_egroup_p(gid_t);
+
+/*
+ * The common credentials for a thread group
+ * - shared by CLONE_THREAD
+ */
+#ifdef CONFIG_KEYS
+struct thread_group_cred {
+       atomic_t        usage;
+       pid_t           tgid;                   /* thread group process ID */
+       spinlock_t      lock;
+       struct key      *session_keyring;       /* keyring inherited over fork */
+       struct key      *process_keyring;       /* keyring private to this process */
+       struct rcu_head rcu;                    /* RCU deletion hook */
+};
+#endif
+
+/*
+ * The security context of a task
+ *
+ * The parts of the context break down into two categories:
+ *
+ *  (1) The objective context of a task.  These parts are used when some other
+ *     task is attempting to affect this one.
+ *
+ *  (2) The subjective context.  These details are used when the task is acting
+ *     upon another object, be that a file, a task, a key or whatever.
+ *
+ * Note that some members of this structure belong to both categories - the
+ * LSM security pointer for instance.
+ *
+ * A task has two security pointers.  task->real_cred points to the objective
+ * context that defines that task's actual details.  The objective part of this
+ * context is used whenever that task is acted upon.
+ *
+ * task->cred points to the subjective context that defines the details of how
+ * that task is going to act upon another object.  This may be overridden
+ * temporarily to point to another security context, but normally points to the
+ * same context as task->real_cred.
+ */
+struct cred {
+       atomic_t        usage;
+       uid_t           uid;            /* real UID of the task */
+       gid_t           gid;            /* real GID of the task */
+       uid_t           suid;           /* saved UID of the task */
+       gid_t           sgid;           /* saved GID of the task */
+       uid_t           euid;           /* effective UID of the task */
+       gid_t           egid;           /* effective GID of the task */
+       uid_t           fsuid;          /* UID for VFS ops */
+       gid_t           fsgid;          /* GID for VFS ops */
+       unsigned        securebits;     /* SUID-less security management */
+       kernel_cap_t    cap_inheritable; /* caps our children can inherit */
+       kernel_cap_t    cap_permitted;  /* caps we're permitted */
+       kernel_cap_t    cap_effective;  /* caps we can actually use */
+       kernel_cap_t    cap_bset;       /* capability bounding set */
+#ifdef CONFIG_KEYS
+       unsigned char   jit_keyring;    /* default keyring to attach requested
+                                        * keys to */
+       struct key      *thread_keyring; /* keyring private to this thread */
+       struct key      *request_key_auth; /* assumed request_key authority */
+       struct thread_group_cred *tgcred; /* thread-group shared credentials */
+#endif
+#ifdef CONFIG_SECURITY
+       void            *security;      /* subjective LSM security */
+#endif
+       struct user_struct *user;       /* real user ID subscription */
+       struct group_info *group_info;  /* supplementary groups for euid/fsgid */
+       struct rcu_head rcu;            /* RCU deletion hook */
+};
+
+extern void __put_cred(struct cred *);
+extern int copy_creds(struct task_struct *, unsigned long);
+extern struct cred *prepare_creds(void);
+extern struct cred *prepare_exec_creds(void);
+extern struct cred *prepare_usermodehelper_creds(void);
+extern int commit_creds(struct cred *);
+extern void abort_creds(struct cred *);
+extern const struct cred *override_creds(const struct cred *);
+extern void revert_creds(const struct cred *);
+extern struct cred *prepare_kernel_cred(struct task_struct *);
+extern int change_create_files_as(struct cred *, struct inode *);
+extern int set_security_override(struct cred *, u32);
+extern int set_security_override_from_ctx(struct cred *, const char *);
+extern int set_create_files_as(struct cred *, struct inode *);
+extern void __init cred_init(void);
+
+/**
+ * get_new_cred - Get a reference on a new set of credentials
+ * @cred: The new credentials to reference
+ *
+ * Get a reference on the specified set of new credentials.  The caller must
+ * release the reference.
+ */
+static inline struct cred *get_new_cred(struct cred *cred)
+{
+       atomic_inc(&cred->usage);
+       return cred;
+}
+
+/**
+ * get_cred - Get a reference on a set of credentials
+ * @cred: The credentials to reference
+ *
+ * Get a reference on the specified set of credentials.  The caller must
+ * release the reference.
+ *
+ * This is used to deal with a committed set of credentials.  Although the
+ * pointer is const, this will temporarily discard the const and increment the
+ * usage count.  The purpose of this is to attempt to catch at compile time the
+ * accidental alteration of a set of credentials that should be considered
+ * immutable.
+ */
+static inline const struct cred *get_cred(const struct cred *cred)
+{
+       return get_new_cred((struct cred *) cred);
+}
+
+/**
+ * put_cred - Release a reference to a set of credentials
+ * @cred: The credentials to release
+ *
+ * Release a reference to a set of credentials, deleting them when the last ref
+ * is released.
+ *
+ * This takes a const pointer to a set of credentials because the credentials
+ * on task_struct are attached by const pointers to prevent accidental
+ * alteration of otherwise immutable credential sets.
+ */
+static inline void put_cred(const struct cred *_cred)
+{
+       struct cred *cred = (struct cred *) _cred;
+
+       BUG_ON(atomic_read(&(cred)->usage) <= 0);
+       if (atomic_dec_and_test(&(cred)->usage))
+               __put_cred(cred);
+}
+
+/**
+ * current_cred - Access the current task's subjective credentials
+ *
+ * Access the subjective credentials of the current task.
+ */
+#define current_cred() \
+       (current->cred)
+
+/**
+ * __task_cred - Access a task's objective credentials
+ * @task: The task to query
+ *
+ * Access the objective credentials of a task.  The caller must hold the RCU
+ * readlock.
+ *
+ * The caller must make sure task doesn't go away, either by holding a ref on
+ * task or by holding tasklist_lock to prevent it from being unlinked.
+ */
+#define __task_cred(task) \
+       ((const struct cred *)(rcu_dereference((task)->real_cred)))
+
+/**
+ * get_task_cred - Get another task's objective credentials
+ * @task: The task to query
+ *
+ * Get the objective credentials of a task, pinning them so that they can't go
+ * away.  Accessing a task's credentials directly is not permitted.
+ *
+ * The caller must make sure task doesn't go away, either by holding a ref on
+ * task or by holding tasklist_lock to prevent it from being unlinked.
+ */
+#define get_task_cred(task)                            \
+({                                                     \
+       struct cred *__cred;                            \
+       rcu_read_lock();                                \
+       __cred = (struct cred *) __task_cred((task));   \
+       get_cred(__cred);                               \
+       rcu_read_unlock();                              \
+       __cred;                                         \
+})
+
+/**
+ * get_current_cred - Get the current task's subjective credentials
+ *
+ * Get the subjective credentials of the current task, pinning them so that
+ * they can't go away.  Accessing the current task's credentials directly is
+ * not permitted.
+ */
+#define get_current_cred()                             \
+       (get_cred(current_cred()))
+
+/**
+ * get_current_user - Get the current task's user_struct
+ *
+ * Get the user record of the current task, pinning it so that it can't go
+ * away.
+ */
+#define get_current_user()                             \
+({                                                     \
+       struct user_struct *__u;                        \
+       struct cred *__cred;                            \
+       __cred = (struct cred *) current_cred();        \
+       __u = get_uid(__cred->user);                    \
+       __u;                                            \
+})
+
+/**
+ * get_current_groups - Get the current task's supplementary group list
+ *
+ * Get the supplementary group list of the current task, pinning it so that it
+ * can't go away.
+ */
+#define get_current_groups()                           \
+({                                                     \
+       struct group_info *__groups;                    \
+       struct cred *__cred;                            \
+       __cred = (struct cred *) current_cred();        \
+       __groups = get_group_info(__cred->group_info);  \
+       __groups;                                       \
+})
+
+#define task_cred_xxx(task, xxx)                       \
+({                                                     \
+       __typeof__(((struct cred *)NULL)->xxx) ___val;  \
+       rcu_read_lock();                                \
+       ___val = __task_cred((task))->xxx;              \
+       rcu_read_unlock();                              \
+       ___val;                                         \
+})
+
+#define task_uid(task)         (task_cred_xxx((task), uid))
+#define task_euid(task)                (task_cred_xxx((task), euid))
+
+#define current_cred_xxx(xxx)                  \
+({                                             \
+       current->cred->xxx;                     \
+})
+
+#define current_uid()          (current_cred_xxx(uid))
+#define current_gid()          (current_cred_xxx(gid))
+#define current_euid()         (current_cred_xxx(euid))
+#define current_egid()         (current_cred_xxx(egid))
+#define current_suid()         (current_cred_xxx(suid))
+#define current_sgid()         (current_cred_xxx(sgid))
+#define current_fsuid()        (current_cred_xxx(fsuid))
+#define current_fsgid()        (current_cred_xxx(fsgid))
+#define current_cap()          (current_cred_xxx(cap_effective))
+#define current_user()         (current_cred_xxx(user))
+#define current_user_ns()      (current_cred_xxx(user)->user_ns)
+#define current_security()     (current_cred_xxx(security))
 
 #define current_uid_gid(_uid, _gid)            \
 do {                                           \
-       *(_uid) = current->uid;                 \
-       *(_gid) = current->gid;                 \
+       const struct cred *__cred;              \
+       __cred = current_cred();                \
+       *(_uid) = __cred->uid;                  \
+       *(_gid) = __cred->gid;                  \
 } while(0)
 
-#define current_euid_egid(_uid, _gid)          \
+#define current_euid_egid(_euid, _egid)                \
 do {                                           \
-       *(_uid) = current->euid;                \
-       *(_gid) = current->egid;                \
+       const struct cred *__cred;              \
+       __cred = current_cred();                \
+       *(_euid) = __cred->euid;                \
+       *(_egid) = __cred->egid;                \
 } while(0)
 
-#define current_fsuid_fsgid(_uid, _gid)                \
+#define current_fsuid_fsgid(_fsuid, _fsgid)    \
 do {                                           \
-       *(_uid) = current->fsuid;               \
-       *(_gid) = current->fsgid;               \
+       const struct cred *__cred;              \
+       __cred = current_cred();                \
+       *(_fsuid) = __cred->fsuid;              \
+       *(_fsgid) = __cred->fsgid;              \
 } while(0)
 
 #endif /* _LINUX_CRED_H */
index 3d2317e..3bacd71 100644 (file)
@@ -36,7 +36,8 @@
 #define CRYPTO_ALG_TYPE_ABLKCIPHER     0x00000005
 #define CRYPTO_ALG_TYPE_GIVCIPHER      0x00000006
 #define CRYPTO_ALG_TYPE_DIGEST         0x00000008
-#define CRYPTO_ALG_TYPE_HASH           0x00000009
+#define CRYPTO_ALG_TYPE_HASH           0x00000008
+#define CRYPTO_ALG_TYPE_SHASH          0x00000009
 #define CRYPTO_ALG_TYPE_AHASH          0x0000000a
 #define CRYPTO_ALG_TYPE_RNG            0x0000000c
 
@@ -220,6 +221,7 @@ struct ablkcipher_alg {
 
 struct ahash_alg {
        int (*init)(struct ahash_request *req);
+       int (*reinit)(struct ahash_request *req);
        int (*update)(struct ahash_request *req);
        int (*final)(struct ahash_request *req);
        int (*digest)(struct ahash_request *req);
@@ -480,6 +482,8 @@ struct crypto_tfm {
                struct compress_tfm compress;
                struct rng_tfm rng;
        } crt_u;
+
+       void (*exit)(struct crypto_tfm *tfm);
        
        struct crypto_alg *__crt_alg;
 
@@ -544,7 +548,9 @@ struct crypto_attr_u32 {
  * Transform user interface.
  */
  
-struct crypto_tfm *crypto_alloc_tfm(const char *alg_name, u32 tfm_flags);
+struct crypto_tfm *crypto_alloc_tfm(const char *alg_name,
+                                   const struct crypto_type *frontend,
+                                   u32 type, u32 mask);
 struct crypto_tfm *crypto_alloc_base(const char *alg_name, u32 type, u32 mask);
 void crypto_free_tfm(struct crypto_tfm *tfm);
 
index 75a81ea..1ee63df 100644 (file)
@@ -888,7 +888,7 @@ struct fb_info {
 #define fb_writeq sbus_writeq
 #define fb_memset sbus_memset_io
 
-#elif defined(__i386__) || defined(__alpha__) || defined(__x86_64__) || defined(__hppa__) || (defined(__sh__) && !defined(__SH5__)) || defined(__powerpc__) || defined(__avr32__)
+#elif defined(__i386__) || defined(__alpha__) || defined(__x86_64__) || defined(__hppa__) || defined(__sh__) || defined(__powerpc__) || defined(__avr32__)
 
 #define fb_readb __raw_readb
 #define fb_readw __raw_readw
index 4a853ef..195a8cb 100644 (file)
@@ -316,6 +316,7 @@ struct poll_table_struct;
 struct kstatfs;
 struct vm_area_struct;
 struct vfsmount;
+struct cred;
 
 extern void __init inode_init(void);
 extern void __init inode_init_early(void);
@@ -827,7 +828,7 @@ struct file {
        fmode_t                 f_mode;
        loff_t                  f_pos;
        struct fown_struct      f_owner;
-       unsigned int            f_uid, f_gid;
+       const struct cred       *f_cred;
        struct file_ra_state    f_ra;
 
        u64                     f_version;
@@ -1194,7 +1195,7 @@ enum {
 #define has_fs_excl() atomic_read(&current->fs_excl)
 
 #define is_owner_or_cap(inode) \
-       ((current->fsuid == (inode)->i_uid) || capable(CAP_FOWNER))
+       ((current_fsuid() == (inode)->i_uid) || capable(CAP_FOWNER))
 
 /* not quite ready to be deprecated, but... */
 extern void lock_super(struct super_block *);
@@ -1674,7 +1675,8 @@ extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
 extern long do_sys_open(int dfd, const char __user *filename, int flags,
                        int mode);
 extern struct file *filp_open(const char *, int, int);
-extern struct file * dentry_open(struct dentry *, struct vfsmount *, int);
+extern struct file * dentry_open(struct dentry *, struct vfsmount *, int,
+                                const struct cred *);
 extern int filp_close(struct file *, fl_owner_t id);
 extern char * getname(const char __user *);
 
index 23fd890..959f552 100644 (file)
@@ -57,7 +57,6 @@ extern struct nsproxy init_nsproxy;
        .mnt_ns         = NULL,                                         \
        INIT_NET_NS(net_ns)                                             \
        INIT_IPC_NS(ipc_ns)                                             \
-       .user_ns        = &init_user_ns,                                \
 }
 
 #define INIT_SIGHAND(sighand) {                                                \
@@ -113,6 +112,8 @@ extern struct group_info init_groups;
 # define CAP_INIT_BSET  CAP_INIT_EFF_SET
 #endif
 
+extern struct cred init_cred;
+
 /*
  *  INIT_TASK is used to set up the first task table, touch at
  * your own risk!. Base=0, limit=0x1fffff (=2MB)
@@ -147,13 +148,10 @@ extern struct group_info init_groups;
        .children       = LIST_HEAD_INIT(tsk.children),                 \
        .sibling        = LIST_HEAD_INIT(tsk.sibling),                  \
        .group_leader   = &tsk,                                         \
-       .group_info     = &init_groups,                                 \
-       .cap_effective  = CAP_INIT_EFF_SET,                             \
-       .cap_inheritable = CAP_INIT_INH_SET,                            \
-       .cap_permitted  = CAP_FULL_SET,                                 \
-       .cap_bset       = CAP_INIT_BSET,                                \
-       .securebits     = SECUREBITS_DEFAULT,                           \
-       .user           = INIT_USER,                                    \
+       .real_cred      = &init_cred,                                   \
+       .cred           = &init_cred,                                   \
+       .cred_exec_mutex =                                              \
+                __MUTEX_INITIALIZER(tsk.cred_exec_mutex),              \
        .comm           = "swapper",                                    \
        .thread         = INIT_THREAD,                                  \
        .fs             = &init_fs,                                     \
index 5341e82..9a6355f 100644 (file)
@@ -659,6 +659,8 @@ struct input_absinfo {
 #define SW_RADIO               SW_RFKILL_ALL   /* deprecated */
 #define SW_MICROPHONE_INSERT   0x04  /* set = inserted */
 #define SW_DOCK                        0x05  /* set = plugged into dock */
+#define SW_LINEOUT_INSERT      0x06  /* set = inserted */
+#define SW_JACK_PHYSICAL_INSERT 0x07  /* set = mechanical switch set */
 #define SW_MAX                 0x0f
 #define SW_CNT                 (SW_MAX+1)
 
diff --git a/include/linux/key-ui.h b/include/linux/key-ui.h
deleted file mode 100644 (file)
index e8b8a7a..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/* key-ui.h: key userspace interface stuff
- *
- * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#ifndef _LINUX_KEY_UI_H
-#define _LINUX_KEY_UI_H
-
-#include <linux/key.h>
-
-/* the key tree */
-extern struct rb_root key_serial_tree;
-extern spinlock_t key_serial_lock;
-
-/* required permissions */
-#define        KEY_VIEW        0x01    /* require permission to view attributes */
-#define        KEY_READ        0x02    /* require permission to read content */
-#define        KEY_WRITE       0x04    /* require permission to update / modify */
-#define        KEY_SEARCH      0x08    /* require permission to search (keyring) or find (key) */
-#define        KEY_LINK        0x10    /* require permission to link */
-#define        KEY_SETATTR     0x20    /* require permission to change attributes */
-#define        KEY_ALL         0x3f    /* all the above permissions */
-
-/*
- * the keyring payload contains a list of the keys to which the keyring is
- * subscribed
- */
-struct keyring_list {
-       struct rcu_head rcu;            /* RCU deletion hook */
-       unsigned short  maxkeys;        /* max keys this list can hold */
-       unsigned short  nkeys;          /* number of keys currently held */
-       unsigned short  delkey;         /* key to be unlinked by RCU */
-       struct key      *keys[0];
-};
-
-/*
- * check to see whether permission is granted to use a key in the desired way
- */
-extern int key_task_permission(const key_ref_t key_ref,
-                              struct task_struct *context,
-                              key_perm_t perm);
-
-static inline int key_permission(const key_ref_t key_ref, key_perm_t perm)
-{
-       return key_task_permission(key_ref, current, perm);
-}
-
-extern key_ref_t lookup_user_key(struct task_struct *context,
-                                key_serial_t id, int create, int partial,
-                                key_perm_t perm);
-
-extern long join_session_keyring(const char *name);
-
-extern struct key_type *key_type_lookup(const char *type);
-extern void key_type_put(struct key_type *ktype);
-
-#define key_negative_timeout   60      /* default timeout on a negative key's existence */
-
-
-#endif /* _LINUX_KEY_UI_H */
index 1b70e35..21d32a1 100644 (file)
@@ -73,6 +73,7 @@ struct key;
 struct seq_file;
 struct user_struct;
 struct signal_struct;
+struct cred;
 
 struct key_type;
 struct key_owner;
@@ -181,7 +182,7 @@ struct key {
 extern struct key *key_alloc(struct key_type *type,
                             const char *desc,
                             uid_t uid, gid_t gid,
-                            struct task_struct *ctx,
+                            const struct cred *cred,
                             key_perm_t perm,
                             unsigned long flags);
 
@@ -249,7 +250,7 @@ extern int key_unlink(struct key *keyring,
                      struct key *key);
 
 extern struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
-                                struct task_struct *ctx,
+                                const struct cred *cred,
                                 unsigned long flags,
                                 struct key *dest);
 
@@ -276,24 +277,11 @@ extern ctl_table key_sysctls[];
 /*
  * the userspace interface
  */
-extern void switch_uid_keyring(struct user_struct *new_user);
-extern int copy_keys(unsigned long clone_flags, struct task_struct *tsk);
-extern int copy_thread_group_keys(struct task_struct *tsk);
-extern void exit_keys(struct task_struct *tsk);
-extern void exit_thread_group_keys(struct signal_struct *tg);
-extern int suid_keys(struct task_struct *tsk);
-extern int exec_keys(struct task_struct *tsk);
+extern int install_thread_keyring_to_cred(struct cred *cred);
 extern void key_fsuid_changed(struct task_struct *tsk);
 extern void key_fsgid_changed(struct task_struct *tsk);
 extern void key_init(void);
 
-#define __install_session_keyring(tsk, keyring)                        \
-({                                                             \
-       struct key *old_session = tsk->signal->session_keyring; \
-       tsk->signal->session_keyring = keyring;                 \
-       old_session;                                            \
-})
-
 #else /* CONFIG_KEYS */
 
 #define key_validate(k)                        0
@@ -302,17 +290,9 @@ extern void key_init(void);
 #define key_revoke(k)                  do { } while(0)
 #define key_put(k)                     do { } while(0)
 #define key_ref_put(k)                 do { } while(0)
-#define make_key_ref(k, p)                     ({ NULL; })
-#define key_ref_to_ptr(k)              ({ NULL; })
+#define make_key_ref(k, p)             NULL
+#define key_ref_to_ptr(k)              NULL
 #define is_key_possessed(k)            0
-#define switch_uid_keyring(u)          do { } while(0)
-#define __install_session_keyring(t, k)        ({ NULL; })
-#define copy_keys(f,t)                 0
-#define copy_thread_group_keys(t)      0
-#define exit_keys(t)                   do { } while(0)
-#define exit_thread_group_keys(tg)     do { } while(0)
-#define suid_keys(t)                   do { } while(0)
-#define exec_keys(t)                   do { } while(0)
 #define key_fsuid_changed(t)           do { } while(0)
 #define key_fsgid_changed(t)           do { } while(0)
 #define key_init()                     do { } while(0)
index 656ee6b..c0688eb 100644 (file)
@@ -1,6 +1,6 @@
 /* keyctl.h: keyctl command IDs
  *
- * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2004, 2008 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
@@ -20,6 +20,7 @@
 #define KEY_SPEC_USER_SESSION_KEYRING  -5      /* - key ID for UID-session keyring */
 #define KEY_SPEC_GROUP_KEYRING         -6      /* - key ID for GID-specific keyring */
 #define KEY_SPEC_REQKEY_AUTH_KEY       -7      /* - key ID for assumed request_key auth key */
+#define KEY_SPEC_REQUESTOR_KEYRING     -8      /* - key ID for request_key() dest keyring */
 
 /* request-key default keyrings */
 #define KEY_REQKEY_DEFL_NO_CHANGE              -1
@@ -30,6 +31,7 @@
 #define KEY_REQKEY_DEFL_USER_KEYRING           4
 #define KEY_REQKEY_DEFL_USER_SESSION_KEYRING   5
 #define KEY_REQKEY_DEFL_GROUP_KEYRING          6
+#define KEY_REQKEY_DEFL_REQUESTOR_KEYRING      7
 
 /* keyctl commands */
 #define KEYCTL_GET_KEYRING_ID          0       /* ask for a keyring's ID */
index 217bb22..af95a1d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * audio.h  --  Audio Driver for Wolfson WM8350 PMIC
  *
- * Copyright 2007 Wolfson Microelectronics PLC
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC
  *
  *  This program is free software; you can redistribute  it and/or modify it
  *  under  the terms of  the GNU General  Public License as published by the
@@ -70,9 +70,9 @@
 #define WM8350_CODEC_ISEL_0_5                   3      /* x0.5 */
 
 #define WM8350_VMID_OFF                         0
-#define WM8350_VMID_500K                        1
-#define WM8350_VMID_100K                        2
-#define WM8350_VMID_10K                         3
+#define WM8350_VMID_300K                        1
+#define WM8350_VMID_50K                         2
+#define WM8350_VMID_5K                          3
 
 /*
  * R40 (0x28) - Clock Control 1
 #define WM8350_IRQ_CODEC_MICSCD                        41
 #define WM8350_IRQ_CODEC_MICD                  42
 
+/*
+ * WM8350 Platform data.
+ *
+ * This must be initialised per platform for best audio performance.
+ * Please see WM8350 datasheet for information.
+ */
+struct wm8350_audio_platform_data {
+       int vmid_discharge_msecs;       /* VMID --> OFF discharge time */
+       int drain_msecs;        /* OFF drain time */
+       int cap_discharge_msecs;        /* Cap ON (from OFF) discharge time */
+       int vmid_charge_msecs;  /* vmid power up time */
+       u32 vmid_s_curve:2;     /* vmid enable s curve speed */
+       u32 dis_out4:2;         /* out4 discharge speed */
+       u32 dis_out3:2;         /* out3 discharge speed */
+       u32 dis_out2:2;         /* out2 discharge speed */
+       u32 dis_out1:2;         /* out1 discharge speed */
+       u32 vroi_out4:1;        /* out4 tie off */
+       u32 vroi_out3:1;        /* out3 tie off */
+       u32 vroi_out2:1;        /* out2 tie off */
+       u32 vroi_out1:1;        /* out1 tie off */
+       u32 vroi_enable:1;      /* enable tie off */
+       u32 codec_current_on:2; /* current level ON */
+       u32 codec_current_standby:2;    /* current level STANDBY */
+       u32 codec_current_charge:2;     /* codec current @ vmid charge */
+};
+
+struct snd_soc_codec;
+
 struct wm8350_codec {
        struct platform_device *pdev;
+       struct snd_soc_codec *codec;
+       struct wm8350_audio_platform_data *platform_data;
 };
 
 #endif
index c8a768e..afad7de 100644 (file)
@@ -27,7 +27,6 @@ struct nsproxy {
        struct ipc_namespace *ipc_ns;
        struct mnt_namespace *mnt_ns;
        struct pid_namespace *pid_ns;
-       struct user_namespace *user_ns;
        struct net           *net_ns;
 };
 extern struct nsproxy init_nsproxy;
index 55e30d1..9624e2c 100644 (file)
@@ -572,12 +572,6 @@ struct signal_struct {
         */
        struct rlimit rlim[RLIM_NLIMITS];
 
-       /* keep the process-shared keyrings here so that they do the right
-        * thing in threads created with CLONE_THREAD */
-#ifdef CONFIG_KEYS
-       struct key *session_keyring;    /* keyring inherited over fork */
-       struct key *process_keyring;    /* keyring private to this process */
-#endif
 #ifdef CONFIG_BSD_PROCESS_ACCT
        struct pacct_struct pacct;      /* per-process accounting information */
 #endif
@@ -648,6 +642,7 @@ struct user_struct {
        /* Hash table maintenance information */
        struct hlist_node uidhash_node;
        uid_t uid;
+       struct user_namespace *user_ns;
 
 #ifdef CONFIG_USER_SCHED
        struct task_group *tg;
@@ -665,6 +660,7 @@ extern struct user_struct *find_user(uid_t);
 extern struct user_struct root_user;
 #define INIT_USER (&root_user)
 
+
 struct backing_dev_info;
 struct reclaim_state;
 
@@ -888,38 +884,7 @@ partition_sched_domains(int ndoms_new, cpumask_t *doms_new,
 #endif /* !CONFIG_SMP */
 
 struct io_context;                     /* See blkdev.h */
-#define NGROUPS_SMALL          32
-#define NGROUPS_PER_BLOCK      ((unsigned int)(PAGE_SIZE / sizeof(gid_t)))
-struct group_info {
-       int ngroups;
-       atomic_t usage;
-       gid_t small_block[NGROUPS_SMALL];
-       int nblocks;
-       gid_t *blocks[0];
-};
 
-/*
- * get_group_info() must be called with the owning task locked (via task_lock())
- * when task != current.  The reason being that the vast majority of callers are
- * looking at current->group_info, which can not be changed except by the
- * current task.  Changing current->group_info requires the task lock, too.
- */
-#define get_group_info(group_info) do { \
-       atomic_inc(&(group_info)->usage); \
-} while (0)
-
-#define put_group_info(group_info) do { \
-       if (atomic_dec_and_test(&(group_info)->usage)) \
-               groups_free(group_info); \
-} while (0)
-
-extern struct group_info *groups_alloc(int gidsetsize);
-extern void groups_free(struct group_info *group_info);
-extern int set_current_groups(struct group_info *group_info);
-extern int groups_search(struct group_info *group_info, gid_t grp);
-/* access the groups "array" with this macro */
-#define GROUP_AT(gi, i) \
-    ((gi)->blocks[(i)/NGROUPS_PER_BLOCK][(i)%NGROUPS_PER_BLOCK])
 
 #ifdef ARCH_HAS_PREFETCH_SWITCH_STACK
 extern void prefetch_stack(struct task_struct *t);
@@ -1186,17 +1151,12 @@ struct task_struct {
        struct list_head cpu_timers[3];
 
 /* process credentials */
-       uid_t uid,euid,suid,fsuid;
-       gid_t gid,egid,sgid,fsgid;
-       struct group_info *group_info;
-       kernel_cap_t   cap_effective, cap_inheritable, cap_permitted, cap_bset;
-       struct user_struct *user;
-       unsigned securebits;
-#ifdef CONFIG_KEYS
-       unsigned char jit_keyring;      /* default keyring to attach requested keys to */
-       struct key *request_key_auth;   /* assumed request_key authority */
-       struct key *thread_keyring;     /* keyring private to this thread */
-#endif
+       const struct cred *real_cred;   /* objective and real subjective task
+                                        * credentials (COW) */
+       const struct cred *cred;        /* effective (overridable) subjective task
+                                        * credentials (COW) */
+       struct mutex cred_exec_mutex;   /* execve vs ptrace cred calculation mutex */
+
        char comm[TASK_COMM_LEN]; /* executable name excluding path
                                     - access with [gs]et_task_comm (which lock
                                       it with task_lock())
@@ -1233,9 +1193,6 @@ struct task_struct {
        int (*notifier)(void *priv);
        void *notifier_data;
        sigset_t *notifier_mask;
-#ifdef CONFIG_SECURITY
-       void *security;
-#endif
        struct audit_context *audit_context;
 #ifdef CONFIG_AUDITSYSCALL
        uid_t loginuid;
@@ -1775,7 +1732,6 @@ static inline struct user_struct *get_uid(struct user_struct *u)
        return u;
 }
 extern void free_uid(struct user_struct *);
-extern void switch_uid(struct user_struct *);
 extern void release_uids(struct user_namespace *ns);
 
 #include <asm/current.h>
@@ -1794,9 +1750,6 @@ extern void wake_up_new_task(struct task_struct *tsk,
 extern void sched_fork(struct task_struct *p, int clone_flags);
 extern void sched_dead(struct task_struct *p);
 
-extern int in_group_p(gid_t);
-extern int in_egroup_p(gid_t);
-
 extern void proc_caches_init(void);
 extern void flush_signals(struct task_struct *);
 extern void ignore_signals(struct task_struct *);
@@ -1928,6 +1881,8 @@ static inline unsigned long wait_task_inactive(struct task_struct *p,
 #define for_each_process(p) \
        for (p = &init_task ; (p = next_task(p)) != &init_task ; )
 
+extern bool is_single_threaded(struct task_struct *);
+
 /*
  * Careful: do_each_thread/while_each_thread is a double loop so
  *          'break' will not work as expected - use goto instead.
index 92f09bd..d2c5ed8 100644 (file)
@@ -32,7 +32,7 @@
    setting is locked or not. A setting which is locked cannot be
    changed from user-level. */
 #define issecure_mask(X)       (1 << (X))
-#define issecure(X)            (issecure_mask(X) & current->securebits)
+#define issecure(X)            (issecure_mask(X) & current_cred_xxx(securebits))
 
 #define SECURE_ALL_BITS                (issecure_mask(SECURE_NOROOT) | \
                                 issecure_mask(SECURE_NO_SETUID_FIXUP) | \
index e3d4ecd..3416cb8 100644 (file)
 /* Maximum number of letters for an LSM name string */
 #define SECURITY_NAME_MAX      10
 
+/* If capable should audit the security request */
+#define SECURITY_CAP_NOAUDIT 0
+#define SECURITY_CAP_AUDIT 1
+
 struct ctl_table;
 struct audit_krule;
 
@@ -44,25 +48,25 @@ struct audit_krule;
  * These functions are in security/capability.c and are used
  * as the default capabilities functions
  */
-extern int cap_capable(struct task_struct *tsk, int cap);
+extern int cap_capable(struct task_struct *tsk, int cap, int audit);
 extern int cap_settime(struct timespec *ts, struct timezone *tz);
 extern int cap_ptrace_may_access(struct task_struct *child, unsigned int mode);
 extern int cap_ptrace_traceme(struct task_struct *parent);
 extern int cap_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
-extern int cap_capset_check(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
-extern void cap_capset_set(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
-extern int cap_bprm_set_security(struct linux_binprm *bprm);
-extern void cap_bprm_apply_creds(struct linux_binprm *bprm, int unsafe);
+extern int cap_capset(struct cred *new, const struct cred *old,
+                     const kernel_cap_t *effective,
+                     const kernel_cap_t *inheritable,
+                     const kernel_cap_t *permitted);
+extern int cap_bprm_set_creds(struct linux_binprm *bprm);
 extern int cap_bprm_secureexec(struct linux_binprm *bprm);
 extern int cap_inode_setxattr(struct dentry *dentry, const char *name,
                              const void *value, size_t size, int flags);
 extern int cap_inode_removexattr(struct dentry *dentry, const char *name);
 extern int cap_inode_need_killpriv(struct dentry *dentry);
 extern int cap_inode_killpriv(struct dentry *dentry);
-extern int cap_task_post_setuid(uid_t old_ruid, uid_t old_euid, uid_t old_suid, int flags);
-extern void cap_task_reparent_to_init(struct task_struct *p);
+extern int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags);
 extern int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
-                         unsigned long arg4, unsigned long arg5, long *rc_p);
+                         unsigned long arg4, unsigned long arg5);
 extern int cap_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp);
 extern int cap_task_setioprio(struct task_struct *p, int ioprio);
 extern int cap_task_setnice(struct task_struct *p, int nice);
@@ -105,7 +109,7 @@ extern unsigned long mmap_min_addr;
 struct sched_param;
 struct request_sock;
 
-/* bprm_apply_creds unsafe reasons */
+/* bprm->unsafe reasons */
 #define LSM_UNSAFE_SHARE       1
 #define LSM_UNSAFE_PTRACE      2
 #define LSM_UNSAFE_PTRACE_CAP  4
@@ -149,36 +153,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *
  * Security hooks for program execution operations.
  *
- * @bprm_alloc_security:
- *     Allocate and attach a security structure to the @bprm->security field.
- *     The security field is initialized to NULL when the bprm structure is
- *     allocated.
- *     @bprm contains the linux_binprm structure to be modified.
- *     Return 0 if operation was successful.
- * @bprm_free_security:
- *     @bprm contains the linux_binprm structure to be modified.
- *     Deallocate and clear the @bprm->security field.
- * @bprm_apply_creds:
- *     Compute and set the security attributes of a process being transformed
- *     by an execve operation based on the old attributes (current->security)
- *     and the information saved in @bprm->security by the set_security hook.
- *     Since this hook function (and its caller) are void, this hook can not
- *     return an error.  However, it can leave the security attributes of the
- *     process unchanged if an access failure occurs at this point.
- *     bprm_apply_creds is called under task_lock.  @unsafe indicates various
- *     reasons why it may be unsafe to change security state.
- *     @bprm contains the linux_binprm structure.
- * @bprm_post_apply_creds:
- *     Runs after bprm_apply_creds with the task_lock dropped, so that
- *     functions which cannot be called safely under the task_lock can
- *     be used.  This hook is a good place to perform state changes on
- *     the process such as closing open file descriptors to which access
- *     is no longer granted if the attributes were changed.
- *     Note that a security module might need to save state between
- *     bprm_apply_creds and bprm_post_apply_creds to store the decision
- *     on whether the process may proceed.
- *     @bprm contains the linux_binprm structure.
- * @bprm_set_security:
+ * @bprm_set_creds:
  *     Save security information in the bprm->security field, typically based
  *     on information about the bprm->file, for later use by the apply_creds
  *     hook.  This hook may also optionally check permissions (e.g. for
@@ -191,15 +166,30 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     @bprm contains the linux_binprm structure.
  *     Return 0 if the hook is successful and permission is granted.
  * @bprm_check_security:
- *     This hook mediates the point when a search for a binary handler will
- *     begin.  It allows a check the @bprm->security value which is set in
- *     the preceding set_security call.  The primary difference from
- *     set_security is that the argv list and envp list are reliably
- *     available in @bprm.  This hook may be called multiple times
- *     during a single execve; and in each pass set_security is called
- *     first.
+ *     This hook mediates the point when a search for a binary handler will
+ *     begin.  It allows a check the @bprm->security value which is set in the
+ *     preceding set_creds call.  The primary difference from set_creds is
+ *     that the argv list and envp list are reliably available in @bprm.  This
+ *     hook may be called multiple times during a single execve; and in each
+ *     pass set_creds is called first.
  *     @bprm contains the linux_binprm structure.
  *     Return 0 if the hook is successful and permission is granted.
+ * @bprm_committing_creds:
+ *     Prepare to install the new security attributes of a process being
+ *     transformed by an execve operation, based on the old credentials
+ *     pointed to by @current->cred and the information set in @bprm->cred by
+ *     the bprm_set_creds hook.  @bprm points to the linux_binprm structure.
+ *     This hook is a good place to perform state changes on the process such
+ *     as closing open file descriptors to which access will no longer be
+ *     granted when the attributes are changed.  This is called immediately
+ *     before commit_creds().
+ * @bprm_committed_creds:
+ *     Tidy up after the installation of the new security attributes of a
+ *     process being transformed by an execve operation.  The new credentials
+ *     have, by this point, been set to @current->cred.  @bprm points to the
+ *     linux_binprm structure.  This hook is a good place to perform state
+ *     changes on the process such as clearing out non-inheritable signal
+ *     state.  This is called immediately after commit_creds().
  * @bprm_secureexec:
  *     Return a boolean value (0 or 1) indicating whether a "secure exec"
  *     is required.  The flag is passed in the auxiliary table
@@ -585,15 +575,31 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     manual page for definitions of the @clone_flags.
  *     @clone_flags contains the flags indicating what should be shared.
  *     Return 0 if permission is granted.
- * @task_alloc_security:
- *     @p contains the task_struct for child process.
- *     Allocate and attach a security structure to the p->security field. The
- *     security field is initialized to NULL when the task structure is
- *     allocated.
- *     Return 0 if operation was successful.
- * @task_free_security:
- *     @p contains the task_struct for process.
- *     Deallocate and clear the p->security field.
+ * @cred_free:
+ *     @cred points to the credentials.
+ *     Deallocate and clear the cred->security field in a set of credentials.
+ * @cred_prepare:
+ *     @new points to the new credentials.
+ *     @old points to the original credentials.
+ *     @gfp indicates the atomicity of any memory allocations.
+ *     Prepare a new set of credentials by copying the data from the old set.
+ * @cred_commit:
+ *     @new points to the new credentials.
+ *     @old points to the original credentials.
+ *     Install a new set of credentials.
+ * @kernel_act_as:
+ *     Set the credentials for a kernel service to act as (subjective context).
+ *     @new points to the credentials to be modified.
+ *     @secid specifies the security ID to be set
+ *     The current task must be the one that nominated @secid.
+ *     Return 0 if successful.
+ * @kernel_create_files_as:
+ *     Set the file creation context in a set of credentials to be the same as
+ *     the objective context of the specified inode.
+ *     @new points to the credentials to be modified.
+ *     @inode points to the inode to use as a reference.
+ *     The current task must be the one that nominated @inode.
+ *     Return 0 if successful.
  * @task_setuid:
  *     Check permission before setting one or more of the user identity
  *     attributes of the current process.  The @flags parameter indicates
@@ -606,15 +612,13 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     @id2 contains a uid.
  *     @flags contains one of the LSM_SETID_* values.
  *     Return 0 if permission is granted.
- * @task_post_setuid:
+ * @task_fix_setuid:
  *     Update the module's state after setting one or more of the user
  *     identity attributes of the current process.  The @flags parameter
  *     indicates which of the set*uid system calls invoked this hook.  If
- *     @flags is LSM_SETID_FS, then @old_ruid is the old fs uid and the other
- *     parameters are not used.
- *     @old_ruid contains the old real uid (or fs uid if LSM_SETID_FS).
- *     @old_euid contains the old effective uid (or -1 if LSM_SETID_FS).
- *     @old_suid contains the old saved uid (or -1 if LSM_SETID_FS).
+ *     @new is the set of credentials that will be installed.  Modifications
+ *     should be made to this rather than to @current->cred.
+ *     @old is the set of credentials that are being replaces
  *     @flags contains one of the LSM_SETID_* values.
  *     Return 0 on success.
  * @task_setgid:
@@ -717,13 +721,8 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     @arg3 contains a argument.
  *     @arg4 contains a argument.
  *     @arg5 contains a argument.
- *      @rc_p contains a pointer to communicate back the forced return code
- *     Return 0 if permission is granted, and non-zero if the security module
- *      has taken responsibility (setting *rc_p) for the prctl call.
- * @task_reparent_to_init:
- *     Set the security attributes in @p->security for a kernel thread that
- *     is being reparented to the init task.
- *     @p contains the task_struct for the kernel thread.
+ *     Return -ENOSYS if no-one wanted to handle this op, any other value to
+ *     cause prctl() to return immediately with that value.
  * @task_to_inode:
  *     Set the security attributes for an inode based on an associated task's
  *     security attributes, e.g. for /proc/pid inodes.
@@ -1000,7 +999,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     See whether a specific operational right is granted to a process on a
  *     key.
  *     @key_ref refers to the key (key pointer + possession attribute bit).
- *     @context points to the process to provide the context against which to
+ *     @cred points to the credentials to provide the context against which to
  *     evaluate the security data on the key.
  *     @perm describes the combination of permissions required of this key.
  *     Return 1 if permission granted, 0 if permission denied and -ve it the
@@ -1162,6 +1161,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     @child process.
  *     Security modules may also want to perform a process tracing check
  *     during an execve in the set_security or apply_creds hooks of
+ *     tracing check during an execve in the bprm_set_creds hook of
  *     binprm_security_ops if the process is being traced and its security
  *     attributes would be changed by the execve.
  *     @child contains the task_struct structure for the target process.
@@ -1185,29 +1185,15 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     @inheritable contains the inheritable capability set.
  *     @permitted contains the permitted capability set.
  *     Return 0 if the capability sets were successfully obtained.
- * @capset_check:
- *     Check permission before setting the @effective, @inheritable, and
- *     @permitted capability sets for the @target process.
- *     Caveat:  @target is also set to current if a set of processes is
- *     specified (i.e. all processes other than current and init or a
- *     particular process group).  Hence, the capset_set hook may need to
- *     revalidate permission to the actual target process.
- *     @target contains the task_struct structure for target process.
- *     @effective contains the effective capability set.
- *     @inheritable contains the inheritable capability set.
- *     @permitted contains the permitted capability set.
- *     Return 0 if permission is granted.
- * @capset_set:
+ * @capset:
  *     Set the @effective, @inheritable, and @permitted capability sets for
- *     the @target process.  Since capset_check cannot always check permission
- *     to the real @target process, this hook may also perform permission
- *     checking to determine if the current process is allowed to set the
- *     capability sets of the @target process.  However, this hook has no way
- *     of returning an error due to the structure of the sys_capset code.
- *     @target contains the task_struct structure for target process.
+ *     the current process.
+ *     @new contains the new credentials structure for target process.
+ *     @old contains the current credentials structure for target process.
  *     @effective contains the effective capability set.
  *     @inheritable contains the inheritable capability set.
  *     @permitted contains the permitted capability set.
+ *     Return 0 and update @new if permission is granted.
  * @capable:
  *     Check whether the @tsk process has the @cap capability.
  *     @tsk contains the task_struct for the process.
@@ -1299,15 +1285,12 @@ struct security_operations {
        int (*capget) (struct task_struct *target,
                       kernel_cap_t *effective,
                       kernel_cap_t *inheritable, kernel_cap_t *permitted);
-       int (*capset_check) (struct task_struct *target,
-                            kernel_cap_t *effective,
-                            kernel_cap_t *inheritable,
-                            kernel_cap_t *permitted);
-       void (*capset_set) (struct task_struct *target,
-                           kernel_cap_t *effective,
-                           kernel_cap_t *inheritable,
-                           kernel_cap_t *permitted);
-       int (*capable) (struct task_struct *tsk, int cap);
+       int (*capset) (struct cred *new,
+                      const struct cred *old,
+                      const kernel_cap_t *effective,
+                      const kernel_cap_t *inheritable,
+                      const kernel_cap_t *permitted);
+       int (*capable) (struct task_struct *tsk, int cap, int audit);
        int (*acct) (struct file *file);
        int (*sysctl) (struct ctl_table *table, int op);
        int (*quotactl) (int cmds, int type, int id, struct super_block *sb);
@@ -1316,18 +1299,16 @@ struct security_operations {
        int (*settime) (struct timespec *ts, struct timezone *tz);
        int (*vm_enough_memory) (struct mm_struct *mm, long pages);
 
-       int (*bprm_alloc_security) (struct linux_binprm *bprm);
-       void (*bprm_free_security) (struct linux_binprm *bprm);
-       void (*bprm_apply_creds) (struct linux_binprm *bprm, int unsafe);
-       void (*bprm_post_apply_creds) (struct linux_binprm *bprm);
-       int (*bprm_set_security) (struct linux_binprm *bprm);
+       int (*bprm_set_creds) (struct linux_binprm *bprm);
        int (*bprm_check_security) (struct linux_binprm *bprm);
        int (*bprm_secureexec) (struct linux_binprm *bprm);
+       void (*bprm_committing_creds) (struct linux_binprm *bprm);
+       void (*bprm_committed_creds) (struct linux_binprm *bprm);
 
        int (*sb_alloc_security) (struct super_block *sb);
        void (*sb_free_security) (struct super_block *sb);
        int (*sb_copy_data) (char *orig, char *copy);
-       int (*sb_kern_mount) (struct super_block *sb, void *data);
+       int (*sb_kern_mount) (struct super_block *sb, int flags, void *data);
        int (*sb_show_options) (struct seq_file *m, struct super_block *sb);
        int (*sb_statfs) (struct dentry *dentry);
        int (*sb_mount) (char *dev_name, struct path *path,
@@ -1406,14 +1387,18 @@ struct security_operations {
        int (*file_send_sigiotask) (struct task_struct *tsk,
                                    struct fown_struct *fown, int sig);
        int (*file_receive) (struct file *file);
-       int (*dentry_open) (struct file *file);
+       int (*dentry_open) (struct file *file, const struct cred *cred);
 
        int (*task_create) (unsigned long clone_flags);
-       int (*task_alloc_security) (struct task_struct *p);
-       void (*task_free_security) (struct task_struct *p);
+       void (*cred_free) (struct cred *cred);
+       int (*cred_prepare)(struct cred *new, const struct cred *old,
+                           gfp_t gfp);
+       void (*cred_commit)(struct cred *new, const struct cred *old);
+       int (*kernel_act_as)(struct cred *new, u32 secid);
+       int (*kernel_create_files_as)(struct cred *new, struct inode *inode);
        int (*task_setuid) (uid_t id0, uid_t id1, uid_t id2, int flags);
-       int (*task_post_setuid) (uid_t old_ruid /* or fsuid */ ,
-                                uid_t old_euid, uid_t old_suid, int flags);
+       int (*task_fix_setuid) (struct cred *new, const struct cred *old,
+                               int flags);
        int (*task_setgid) (gid_t id0, gid_t id1, gid_t id2, int flags);
        int (*task_setpgid) (struct task_struct *p, pid_t pgid);
        int (*task_getpgid) (struct task_struct *p);
@@ -1433,8 +1418,7 @@ struct security_operations {
        int (*task_wait) (struct task_struct *p);
        int (*task_prctl) (int option, unsigned long arg2,
                           unsigned long arg3, unsigned long arg4,
-                          unsigned long arg5, long *rc_p);
-       void (*task_reparent_to_init) (struct task_struct *p);
+                          unsigned long arg5);
        void (*task_to_inode) (struct task_struct *p, struct inode *inode);
 
        int (*ipc_permission) (struct kern_ipc_perm *ipcp, short flag);
@@ -1539,10 +1523,10 @@ struct security_operations {
 
        /* key management security hooks */
 #ifdef CONFIG_KEYS
-       int (*key_alloc) (struct key *key, struct task_struct *tsk, unsigned long flags);
+       int (*key_alloc) (struct key *key, const struct cred *cred, unsigned long flags);
        void (*key_free) (struct key *key);
        int (*key_permission) (key_ref_t key_ref,
-                              struct task_struct *context,
+                              const struct cred *cred,
                               key_perm_t perm);
        int (*key_getsecurity)(struct key *key, char **_buffer);
 #endif /* CONFIG_KEYS */
@@ -1568,15 +1552,12 @@ int security_capget(struct task_struct *target,
                    kernel_cap_t *effective,
                    kernel_cap_t *inheritable,
                    kernel_cap_t *permitted);
-int security_capset_check(struct task_struct *target,
-                         kernel_cap_t *effective,
-                         kernel_cap_t *inheritable,
-                         kernel_cap_t *permitted);
-void security_capset_set(struct task_struct *target,
-                        kernel_cap_t *effective,
-                        kernel_cap_t *inheritable,
-                        kernel_cap_t *permitted);
+int security_capset(struct cred *new, const struct cred *old,
+                   const kernel_cap_t *effective,
+                   const kernel_cap_t *inheritable,
+                   const kernel_cap_t *permitted);
 int security_capable(struct task_struct *tsk, int cap);
+int security_capable_noaudit(struct task_struct *tsk, int cap);
 int security_acct(struct file *file);
 int security_sysctl(struct ctl_table *table, int op);
 int security_quotactl(int cmds, int type, int id, struct super_block *sb);
@@ -1586,17 +1567,15 @@ int security_settime(struct timespec *ts, struct timezone *tz);
 int security_vm_enough_memory(long pages);
 int security_vm_enough_memory_mm(struct mm_struct *mm, long pages);
 int security_vm_enough_memory_kern(long pages);
-int security_bprm_alloc(struct linux_binprm *bprm);
-void security_bprm_free(struct linux_binprm *bprm);
-void security_bprm_apply_creds(struct linux_binprm *bprm, int unsafe);
-void security_bprm_post_apply_creds(struct linux_binprm *bprm);
-int security_bprm_set(struct linux_binprm *bprm);
+int security_bprm_set_creds(struct linux_binprm *bprm);
 int security_bprm_check(struct linux_binprm *bprm);
+void security_bprm_committing_creds(struct linux_binprm *bprm);
+void security_bprm_committed_creds(struct linux_binprm *bprm);
 int security_bprm_secureexec(struct linux_binprm *bprm);
 int security_sb_alloc(struct super_block *sb);
 void security_sb_free(struct super_block *sb);
 int security_sb_copy_data(char *orig, char *copy);
-int security_sb_kern_mount(struct super_block *sb, void *data);
+int security_sb_kern_mount(struct super_block *sb, int flags, void *data);
 int security_sb_show_options(struct seq_file *m, struct super_block *sb);
 int security_sb_statfs(struct dentry *dentry);
 int security_sb_mount(char *dev_name, struct path *path,
@@ -1663,13 +1642,16 @@ int security_file_set_fowner(struct file *file);
 int security_file_send_sigiotask(struct task_struct *tsk,
                                 struct fown_struct *fown, int sig);
 int security_file_receive(struct file *file);
-int security_dentry_open(struct file *file);
+int security_dentry_open(struct file *file, const struct cred *cred);
 int security_task_create(unsigned long clone_flags);
-int security_task_alloc(struct task_struct *p);
-void security_task_free(struct task_struct *p);
+void security_cred_free(struct cred *cred);
+int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp);
+void security_commit_creds(struct cred *new, const struct cred *old);
+int security_kernel_act_as(struct cred *new, u32 secid);
+int security_kernel_create_files_as(struct cred *new, struct inode *inode);
 int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags);
-int security_task_post_setuid(uid_t old_ruid, uid_t old_euid,
-                             uid_t old_suid, int flags);
+int security_task_fix_setuid(struct cred *new, const struct cred *old,
+                            int flags);
 int security_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags);
 int security_task_setpgid(struct task_struct *p, pid_t pgid);
 int security_task_getpgid(struct task_struct *p);
@@ -1688,8 +1670,7 @@ int security_task_kill(struct task_struct *p, struct siginfo *info,
                        int sig, u32 secid);
 int security_task_wait(struct task_struct *p);
 int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
-                        unsigned long arg4, unsigned long arg5, long *rc_p);
-void security_task_reparent_to_init(struct task_struct *p);
+                       unsigned long arg4, unsigned long arg5);
 void security_task_to_inode(struct task_struct *p, struct inode *inode);
 int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag);
 void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid);
@@ -1764,25 +1745,23 @@ static inline int security_capget(struct task_struct *target,
        return cap_capget(target, effective, inheritable, permitted);
 }
 
-static inline int security_capset_check(struct task_struct *target,
-                                        kernel_cap_t *effective,
-                                        kernel_cap_t *inheritable,
-                                        kernel_cap_t *permitted)
+static inline int security_capset(struct cred *new,
+                                  const struct cred *old,
+                                  const kernel_cap_t *effective,
+                                  const kernel_cap_t *inheritable,
+                                  const kernel_cap_t *permitted)
 {
-       return cap_capset_check(target, effective, inheritable, permitted);
+       return cap_capset(new, old, effective, inheritable, permitted);
 }
 
-static inline void security_capset_set(struct task_struct *target,
-                                       kernel_cap_t *effective,
-                                       kernel_cap_t *inheritable,
-                                       kernel_cap_t *permitted)
+static inline int security_capable(struct task_struct *tsk, int cap)
 {
-       cap_capset_set(target, effective, inheritable, permitted);
+       return cap_capable(tsk, cap, SECURITY_CAP_AUDIT);
 }
 
-static inline int security_capable(struct task_struct *tsk, int cap)
+static inline int security_capable_noaudit(struct task_struct *tsk, int cap)
 {
-       return cap_capable(tsk, cap);
+       return cap_capable(tsk, cap, SECURITY_CAP_NOAUDIT);
 }
 
 static inline int security_acct(struct file *file)
@@ -1835,32 +1814,22 @@ static inline int security_vm_enough_memory_kern(long pages)
        return cap_vm_enough_memory(current->mm, pages);
 }
 
-static inline int security_bprm_alloc(struct linux_binprm *bprm)
-{
-       return 0;
-}
-
-static inline void security_bprm_free(struct linux_binprm *bprm)
-{ }
-
-static inline void security_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
+static inline int security_bprm_set_creds(struct linux_binprm *bprm)
 {
-       cap_bprm_apply_creds(bprm, unsafe);
+       return cap_bprm_set_creds(bprm);
 }
 
-static inline void security_bprm_post_apply_creds(struct linux_binprm *bprm)
+static inline int security_bprm_check(struct linux_binprm *bprm)
 {
-       return;
+       return 0;
 }
 
-static inline int security_bprm_set(struct linux_binprm *bprm)
+static inline void security_bprm_committing_creds(struct linux_binprm *bprm)
 {
-       return cap_bprm_set_security(bprm);
 }
 
-static inline int security_bprm_check(struct linux_binprm *bprm)
+static inline void security_bprm_committed_creds(struct linux_binprm *bprm)
 {
-       return 0;
 }
 
 static inline int security_bprm_secureexec(struct linux_binprm *bprm)
@@ -1881,7 +1850,7 @@ static inline int security_sb_copy_data(char *orig, char *copy)
        return 0;
 }
 
-static inline int security_sb_kern_mount(struct super_block *sb, void *data)
+static inline int security_sb_kern_mount(struct super_block *sb, int flags, void *data)
 {
        return 0;
 }
@@ -2177,7 +2146,8 @@ static inline int security_file_receive(struct file *file)
        return 0;
 }
 
-static inline int security_dentry_open(struct file *file)
+static inline int security_dentry_open(struct file *file,
+                                      const struct cred *cred)
 {
        return 0;
 }
@@ -2187,13 +2157,31 @@ static inline int security_task_create(unsigned long clone_flags)
        return 0;
 }
 
-static inline int security_task_alloc(struct task_struct *p)
+static inline void security_cred_free(struct cred *cred)
+{ }
+
+static inline int security_prepare_creds(struct cred *new,
+                                        const struct cred *old,
+                                        gfp_t gfp)
 {
        return 0;
 }
 
-static inline void security_task_free(struct task_struct *p)
-{ }
+static inline void security_commit_creds(struct cred *new,
+                                        const struct cred *old)
+{
+}
+
+static inline int security_kernel_act_as(struct cred *cred, u32 secid)
+{
+       return 0;
+}
+
+static inline int security_kernel_create_files_as(struct cred *cred,
+                                                 struct inode *inode)
+{
+       return 0;
+}
 
 static inline int security_task_setuid(uid_t id0, uid_t id1, uid_t id2,
                                       int flags)
@@ -2201,10 +2189,11 @@ static inline int security_task_setuid(uid_t id0, uid_t id1, uid_t id2,
        return 0;
 }
 
-static inline int security_task_post_setuid(uid_t old_ruid, uid_t old_euid,
-                                           uid_t old_suid, int flags)
+static inline int security_task_fix_setuid(struct cred *new,
+                                          const struct cred *old,
+                                          int flags)
 {
-       return cap_task_post_setuid(old_ruid, old_euid, old_suid, flags);
+       return cap_task_fix_setuid(new, old, flags);
 }
 
 static inline int security_task_setgid(gid_t id0, gid_t id1, gid_t id2,
@@ -2291,14 +2280,9 @@ static inline int security_task_wait(struct task_struct *p)
 static inline int security_task_prctl(int option, unsigned long arg2,
                                      unsigned long arg3,
                                      unsigned long arg4,
-                                     unsigned long arg5, long *rc_p)
-{
-       return cap_task_prctl(option, arg2, arg3, arg3, arg5, rc_p);
-}
-
-static inline void security_task_reparent_to_init(struct task_struct *p)
+                                     unsigned long arg5)
 {
-       cap_task_reparent_to_init(p);
+       return cap_task_prctl(option, arg2, arg3, arg3, arg5);
 }
 
 static inline void security_task_to_inode(struct task_struct *p, struct inode *inode)
@@ -2724,16 +2708,16 @@ static inline void security_skb_classify_flow(struct sk_buff *skb, struct flowi
 #ifdef CONFIG_KEYS
 #ifdef CONFIG_SECURITY
 
-int security_key_alloc(struct key *key, struct task_struct *tsk, unsigned long flags);
+int security_key_alloc(struct key *key, const struct cred *cred, unsigned long flags);
 void security_key_free(struct key *key);
 int security_key_permission(key_ref_t key_ref,
-                           struct task_struct *context, key_perm_t perm);
+                           const struct cred *cred, key_perm_t perm);
 int security_key_getsecurity(struct key *key, char **_buffer);
 
 #else
 
 static inline int security_key_alloc(struct key *key,
-                                    struct task_struct *tsk,
+                                    const struct cred *cred,
                                     unsigned long flags)
 {
        return 0;
@@ -2744,7 +2728,7 @@ static inline void security_key_free(struct key *key)
 }
 
 static inline int security_key_permission(key_ref_t key_ref,
-                                         struct task_struct *context,
+                                         const struct cred *cred,
                                          key_perm_t perm)
 {
        return 0;
index 3b8121d..580700f 100644 (file)
@@ -442,6 +442,7 @@ extern void tty_audit_add_data(struct tty_struct *tty, unsigned char *data,
                               size_t size);
 extern void tty_audit_exit(void);
 extern void tty_audit_fork(struct signal_struct *sig);
+extern void tty_audit_tiocsti(struct tty_struct *tty, char ch);
 extern void tty_audit_push(struct tty_struct *tty);
 extern void tty_audit_push_task(struct task_struct *tsk,
                                        uid_t loginuid, u32 sessionid);
@@ -450,6 +451,9 @@ static inline void tty_audit_add_data(struct tty_struct *tty,
                                      unsigned char *data, size_t size)
 {
 }
+static inline void tty_audit_tiocsti(struct tty_struct *tty, char ch)
+{
+}
 static inline void tty_audit_exit(void)
 {
 }
index b5f41d4..315bcd3 100644 (file)
@@ -12,7 +12,7 @@
 struct user_namespace {
        struct kref             kref;
        struct hlist_head       uidhash_table[UIDHASH_SZ];
-       struct user_struct      *root_user;
+       struct user_struct      *creator;
 };
 
 extern struct user_namespace init_user_ns;
@@ -26,8 +26,7 @@ static inline struct user_namespace *get_user_ns(struct user_namespace *ns)
        return ns;
 }
 
-extern struct user_namespace *copy_user_ns(int flags,
-                                          struct user_namespace *old_ns);
+extern int create_user_ns(struct cred *new);
 extern void free_user_ns(struct kref *kref);
 
 static inline void put_user_ns(struct user_namespace *ns)
@@ -43,13 +42,9 @@ static inline struct user_namespace *get_user_ns(struct user_namespace *ns)
        return &init_user_ns;
 }
 
-static inline struct user_namespace *copy_user_ns(int flags,
-                                                 struct user_namespace *old_ns)
+static inline int create_user_ns(struct cred *new)
 {
-       if (flags & CLONE_NEWUSER)
-               return ERR_PTR(-EINVAL);
-
-       return old_ns;
+       return -EINVAL;
 }
 
 static inline void put_user_ns(struct user_namespace *ns)
index 33e9986..f45bb6e 100644 (file)
@@ -55,8 +55,8 @@ static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
                               struct scm_cookie *scm)
 {
        struct task_struct *p = current;
-       scm->creds.uid = p->uid;
-       scm->creds.gid = p->gid;
+       scm->creds.uid = current_uid();
+       scm->creds.gid = current_gid();
        scm->creds.pid = task_tgid_vnr(p);
        scm->fp = NULL;
        scm->seq = 0;
index 9c309da..251fc1c 100644 (file)
 /* specific - Analog Devices */
 #define AC97_AD_TEST           0x5a    /* test register */
 #define AC97_AD_TEST2          0x5c    /* undocumented test register 2 */
+#define AC97_AD_HPFD_SHIFT     12      /* High Pass Filter Disable */
 #define AC97_AD_CODEC_CFG      0x70    /* codec configuration */
 #define AC97_AD_JACK_SPDIF     0x72    /* Jack Sense & S/PDIF */
 #define AC97_AD_SERIAL_CFG     0x74    /* Serial Configuration */
 #define AC97_AD_MISC           0x76    /* Misc Control Bits */
+#define AC97_AD_VREFD_SHIFT    2       /* V_REFOUT Disable (AD1888) */
 
 /* specific - Cirrus Logic */
 #define AC97_CSR_ACMODE                0x5e    /* AC Mode Register */
index 2c4dc90..1c02ed1 100644 (file)
@@ -575,6 +575,7 @@ enum {
 #define SNDRV_TIMER_GLOBAL_SYSTEM      0
 #define SNDRV_TIMER_GLOBAL_RTC         1
 #define SNDRV_TIMER_GLOBAL_HPET                2
+#define SNDRV_TIMER_GLOBAL_HRTIMER     3
 
 /* info flags */
 #define SNDRV_TIMER_FLG_SLAVE          (1<<0)  /* cannot be controlled */
index 1508c4e..f632484 100644 (file)
@@ -353,7 +353,7 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
  * snd_printk - printk wrapper
  * @fmt: format string
  *
- * Works like print() but prints the file and the line of the caller
+ * Works like printk() but prints the file and the line of the caller
  * when configured with CONFIG_SND_VERBOSE_PRINTK.
  */
 #define snd_printk(fmt, args...) \
@@ -380,18 +380,40 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
        printk(fmt ,##args)
 #endif
 
+/**
+ * snd_BUG - give a BUG warning message and stack trace
+ *
+ * Calls WARN() if CONFIG_SND_DEBUG is set.
+ * Ignored when CONFIG_SND_DEBUG is not set.
+ */
 #define snd_BUG()              WARN(1, "BUG?\n")
+
+/**
+ * snd_BUG_ON - debugging check macro
+ * @cond: condition to evaluate
+ *
+ * When CONFIG_SND_DEBUG is set, this macro evaluates the given condition,
+ * and call WARN() and returns the value if it's non-zero.
+ * 
+ * When CONFIG_SND_DEBUG is not set, this just returns zero, and the given
+ * condition is ignored.
+ *
+ * NOTE: the argument won't be evaluated at all when CONFIG_SND_DEBUG=n.
+ * Thus, don't put any statement that influences on the code behavior,
+ * such as pre/post increment, to the argument of this macro.
+ * If you want to evaluate and give a warning, use standard WARN_ON().
+ */
 #define snd_BUG_ON(cond)       WARN((cond), "BUG? (%s)\n", __stringify(cond))
 
 #else /* !CONFIG_SND_DEBUG */
 
 #define snd_printd(fmt, args...)       do { } while (0)
 #define snd_BUG()                      do { } while (0)
-static inline int __snd_bug_on(void)
+static inline int __snd_bug_on(int cond)
 {
        return 0;
 }
-#define snd_BUG_ON(cond)               __snd_bug_on()  /* always false */
+#define snd_BUG_ON(cond)       __snd_bug_on(0 && (cond))  /* always false */
 
 #endif /* CONFIG_SND_DEBUG */
 
index 8ae72e7..7c2ee1a 100644 (file)
@@ -40,30 +40,34 @@ struct snd_info_buffer {
 struct snd_info_entry;
 
 struct snd_info_entry_text {
-       void (*read) (struct snd_info_entry *entry, struct snd_info_buffer *buffer);
-       void (*write) (struct snd_info_entry *entry, struct snd_info_buffer *buffer);
+       void (*read)(struct snd_info_entry *entry,
+                    struct snd_info_buffer *buffer);
+       void (*write)(struct snd_info_entry *entry,
+                     struct snd_info_buffer *buffer);
 };
 
 struct snd_info_entry_ops {
-       int (*open) (struct snd_info_entry *entry,
-                    unsigned short mode, void **file_private_data);
-       int (*release) (struct snd_info_entry * entry,
-                       unsigned short mode, void *file_private_data);
-       long (*read) (struct snd_info_entry *entry, void *file_private_data,
-                     struct file * file, char __user *buf,
+       int (*open)(struct snd_info_entry *entry,
+                   unsigned short mode, void **file_private_data);
+       int (*release)(struct snd_info_entry *entry,
+                      unsigned short mode, void *file_private_data);
+       long (*read)(struct snd_info_entry *entry, void *file_private_data,
+                    struct file *file, char __user *buf,
+                    unsigned long count, unsigned long pos);
+       long (*write)(struct snd_info_entry *entry, void *file_private_data,
+                     struct file *file, const char __user *buf,
                      unsigned long count, unsigned long pos);
-       long (*write) (struct snd_info_entry *entry, void *file_private_data,
-                      struct file * file, const char __user *buf,
-                      unsigned long count, unsigned long pos);
-       long long (*llseek) (struct snd_info_entry *entry, void *file_private_data,
-                           struct file * file, long long offset, int orig);
-       unsigned int (*poll) (struct snd_info_entry *entry, void *file_private_data,
-                             struct file * file, poll_table * wait);
-       int (*ioctl) (struct snd_info_entry *entry, void *file_private_data,
-                     struct file * file, unsigned int cmd, unsigned long arg);
-       int (*mmap) (struct snd_info_entry *entry, void *file_private_data,
-                    struct inode * inode, struct file * file,
-                    struct vm_area_struct * vma);
+       long long (*llseek)(struct snd_info_entry *entry,
+                           void *file_private_data, struct file *file,
+                           long long offset, int orig);
+       unsigned int(*poll)(struct snd_info_entry *entry,
+                           void *file_private_data, struct file *file,
+                           poll_table *wait);
+       int (*ioctl)(struct snd_info_entry *entry, void *file_private_data,
+                    struct file *file, unsigned int cmd, unsigned long arg);
+       int (*mmap)(struct snd_info_entry *entry, void *file_private_data,
+                   struct inode *inode, struct file *file,
+                   struct vm_area_struct *vma);
 };
 
 struct snd_info_entry {
@@ -106,34 +110,37 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer);
 static inline void snd_card_info_read_oss(struct snd_info_buffer *buffer) {}
 #endif
 
-int snd_iprintf(struct snd_info_buffer * buffer, char *fmt,...) __attribute__ ((format (printf, 2, 3)));
+int snd_iprintf(struct snd_info_buffer *buffer, char *fmt, ...) \
+                               __attribute__ ((format (printf, 2, 3)));
 int snd_info_init(void);
 int snd_info_done(void);
 
-int snd_info_get_line(struct snd_info_buffer * buffer, char *line, int len);
+int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len);
 char *snd_info_get_str(char *dest, char *src, int len);
-struct snd_info_entry *snd_info_create_module_entry(struct module * module,
+struct snd_info_entry *snd_info_create_module_entry(struct module *module,
                                               const char *name,
-                                              struct snd_info_entry * parent);
-struct snd_info_entry *snd_info_create_card_entry(struct snd_card * card,
+                                              struct snd_info_entry *parent);
+struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
                                             const char *name,
-                                            struct snd_info_entry * parent);
-void snd_info_free_entry(struct snd_info_entry * entry);
-int snd_info_store_text(struct snd_info_entry * entry);
-int snd_info_restore_text(struct snd_info_entry * entry);
-
-int snd_info_card_create(struct snd_card * card);
-int snd_info_card_register(struct snd_card * card);
-int snd_info_card_free(struct snd_card * card);
-void snd_info_card_disconnect(struct snd_card * card);
-int snd_info_register(struct snd_info_entry * entry);
+                                            struct snd_info_entry *parent);
+void snd_info_free_entry(struct snd_info_entry *entry);
+int snd_info_store_text(struct snd_info_entry *entry);
+int snd_info_restore_text(struct snd_info_entry *entry);
+
+int snd_info_card_create(struct snd_card *card);
+int snd_info_card_register(struct snd_card *card);
+int snd_info_card_free(struct snd_card *card);
+void snd_info_card_disconnect(struct snd_card *card);
+void snd_info_card_id_change(struct snd_card *card);
+int snd_info_register(struct snd_info_entry *entry);
 
 /* for card drivers */
-int snd_card_proc_new(struct snd_card *card, const char *name, struct snd_info_entry **entryp);
+int snd_card_proc_new(struct snd_card *card, const char *name,
+                     struct snd_info_entry **entryp);
 
 static inline void snd_info_set_text_ops(struct snd_info_entry *entry, 
-                                        void *private_data,
-                                        void (*read)(struct snd_info_entry *, struct snd_info_buffer *))
+       void *private_data,
+       void (*read)(struct snd_info_entry *, struct snd_info_buffer *))
 {
        entry->private_data = private_data;
        entry->c.text.read = read;
@@ -146,21 +153,22 @@ int snd_info_check_reserved_words(const char *str);
 #define snd_seq_root NULL
 #define snd_oss_root NULL
 
-static inline int snd_iprintf(struct snd_info_buffer * buffer, char *fmt,...) { return 0; }
+static inline int snd_iprintf(struct snd_info_buffer *buffer, char *fmt, ...) { return 0; }
 static inline int snd_info_init(void) { return 0; }
 static inline int snd_info_done(void) { return 0; }
 
-static inline int snd_info_get_line(struct snd_info_buffer * buffer, char *line, int len) { return 0; }
+static inline int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) { return 0; }
 static inline char *snd_info_get_str(char *dest, char *src, int len) { return NULL; }
-static inline struct snd_info_entry *snd_info_create_module_entry(struct module * module, const char *name, struct snd_info_entry * parent) { return NULL; }
-static inline struct snd_info_entry *snd_info_create_card_entry(struct snd_card * card, const char *name, struct snd_info_entry * parent) { return NULL; }
-static inline void snd_info_free_entry(struct snd_info_entry * entry) { ; }
-
-static inline int snd_info_card_create(struct snd_card * card) { return 0; }
-static inline int snd_info_card_register(struct snd_card * card) { return 0; }
-static inline int snd_info_card_free(struct snd_card * card) { return 0; }
-static inline void snd_info_card_disconnect(struct snd_card * card) { }
-static inline int snd_info_register(struct snd_info_entry * entry) { return 0; }
+static inline struct snd_info_entry *snd_info_create_module_entry(struct module *module, const char *name, struct snd_info_entry *parent) { return NULL; }
+static inline struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, const char *name, struct snd_info_entry *parent) { return NULL; }
+static inline void snd_info_free_entry(struct snd_info_entry *entry) { ; }
+
+static inline int snd_info_card_create(struct snd_card *card) { return 0; }
+static inline int snd_info_card_register(struct snd_card *card) { return 0; }
+static inline int snd_info_card_free(struct snd_card *card) { return 0; }
+static inline void snd_info_card_disconnect(struct snd_card *card) { }
+static inline void snd_info_card_id_change(struct snd_card *card) { }
+static inline int snd_info_register(struct snd_info_entry *entry) { return 0; }
 
 static inline int snd_card_proc_new(struct snd_card *card, const char *name,
                                    struct snd_info_entry **entryp) { return -EINVAL; }
index b1b2b8b..2e0315c 100644 (file)
@@ -35,6 +35,8 @@ enum snd_jack_types {
        SND_JACK_HEADPHONE      = 0x0001,
        SND_JACK_MICROPHONE     = 0x0002,
        SND_JACK_HEADSET        = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE,
+       SND_JACK_LINEOUT        = 0x0004,
+       SND_JACK_MECHANICAL     = 0x0008, /* If detected separately */
 };
 
 struct snd_jack {
diff --git a/include/sound/l3.h b/include/sound/l3.h
new file mode 100644 (file)
index 0000000..423a08f
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef _L3_H_
+#define _L3_H_ 1
+
+struct l3_pins {
+       void (*setdat)(int);
+       void (*setclk)(int);
+       void (*setmode)(int);
+       int data_hold;
+       int data_setup;
+       int clock_high;
+       int mode_hold;
+       int mode;
+       int mode_setup;
+};
+
+int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len);
+
+#endif
diff --git a/include/sound/s3c24xx_uda134x.h b/include/sound/s3c24xx_uda134x.h
new file mode 100644 (file)
index 0000000..33df4cb
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef _S3C24XX_UDA134X_H_
+#define _S3C24XX_UDA134X_H_ 1
+
+#include <sound/uda134x.h>
+
+struct s3c24xx_uda134x_platform_data {
+       int l3_clk;
+       int l3_mode;
+       int l3_data;
+       void (*power) (int);
+       int model;
+};
+
+#endif
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
new file mode 100644 (file)
index 0000000..24247f7
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * linux/sound/soc-dai.h -- ALSA SoC Layer
+ *
+ * Copyright:  2005-2008 Wolfson Microelectronics. PLC.
+ *
+ * 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.
+ *
+ * Digital Audio Interface (DAI) API.
+ */
+
+#ifndef __LINUX_SND_SOC_DAI_H
+#define __LINUX_SND_SOC_DAI_H
+
+
+#include <linux/list.h>
+
+struct snd_pcm_substream;
+
+/*
+ * DAI hardware audio formats.
+ *
+ * Describes the physical PCM data formating and clocking. Add new formats
+ * to the end.
+ */
+#define SND_SOC_DAIFMT_I2S             0 /* I2S mode */
+#define SND_SOC_DAIFMT_RIGHT_J         1 /* Right Justified mode */
+#define SND_SOC_DAIFMT_LEFT_J          2 /* Left Justified mode */
+#define SND_SOC_DAIFMT_DSP_A           3 /* L data msb after FRM LRC */
+#define SND_SOC_DAIFMT_DSP_B           4 /* L data msb during FRM LRC */
+#define SND_SOC_DAIFMT_AC97            5 /* AC97 */
+
+/* left and right justified also known as MSB and LSB respectively */
+#define SND_SOC_DAIFMT_MSB             SND_SOC_DAIFMT_LEFT_J
+#define SND_SOC_DAIFMT_LSB             SND_SOC_DAIFMT_RIGHT_J
+
+/*
+ * DAI Clock gating.
+ *
+ * DAI bit clocks can be be gated (disabled) when not the DAI is not
+ * sending or receiving PCM data in a frame. This can be used to save power.
+ */
+#define SND_SOC_DAIFMT_CONT            (0 << 4) /* continuous clock */
+#define SND_SOC_DAIFMT_GATED           (1 << 4) /* clock is gated */
+
+/*
+ * DAI Left/Right Clocks.
+ *
+ * Specifies whether the DAI can support different samples for similtanious
+ * playback and capture. This usually requires a seperate physical frame
+ * clock for playback and capture.
+ */
+#define SND_SOC_DAIFMT_SYNC            (0 << 5) /* Tx FRM = Rx FRM */
+#define SND_SOC_DAIFMT_ASYNC           (1 << 5) /* Tx FRM ~ Rx FRM */
+
+/*
+ * TDM
+ *
+ * Time Division Multiplexing. Allows PCM data to be multplexed with other
+ * data on the DAI.
+ */
+#define SND_SOC_DAIFMT_TDM             (1 << 6)
+
+/*
+ * DAI hardware signal inversions.
+ *
+ * Specifies whether the DAI can also support inverted clocks for the specified
+ * format.
+ */
+#define SND_SOC_DAIFMT_NB_NF           (0 << 8) /* normal bit clock + frame */
+#define SND_SOC_DAIFMT_NB_IF           (1 << 8) /* normal bclk + inv frm */
+#define SND_SOC_DAIFMT_IB_NF           (2 << 8) /* invert bclk + nor frm */
+#define SND_SOC_DAIFMT_IB_IF           (3 << 8) /* invert bclk + frm */
+
+/*
+ * DAI hardware clock masters.
+ *
+ * This is wrt the codec, the inverse is true for the interface
+ * i.e. if the codec is clk and frm master then the interface is
+ * clk and frame slave.
+ */
+#define SND_SOC_DAIFMT_CBM_CFM         (0 << 12) /* codec clk & frm master */
+#define SND_SOC_DAIFMT_CBS_CFM         (1 << 12) /* codec clk slave & frm master */
+#define SND_SOC_DAIFMT_CBM_CFS         (2 << 12) /* codec clk master & frame slave */
+#define SND_SOC_DAIFMT_CBS_CFS         (3 << 12) /* codec clk & frm slave */
+
+#define SND_SOC_DAIFMT_FORMAT_MASK     0x000f
+#define SND_SOC_DAIFMT_CLOCK_MASK      0x00f0
+#define SND_SOC_DAIFMT_INV_MASK                0x0f00
+#define SND_SOC_DAIFMT_MASTER_MASK     0xf000
+
+/*
+ * Master Clock Directions
+ */
+#define SND_SOC_CLOCK_IN               0
+#define SND_SOC_CLOCK_OUT              1
+
+struct snd_soc_dai_ops;
+struct snd_soc_dai;
+struct snd_ac97_bus_ops;
+
+/* Digital Audio Interface registration */
+int snd_soc_register_dai(struct snd_soc_dai *dai);
+void snd_soc_unregister_dai(struct snd_soc_dai *dai);
+int snd_soc_register_dais(struct snd_soc_dai *dai, size_t count);
+void snd_soc_unregister_dais(struct snd_soc_dai *dai, size_t count);
+
+/* Digital Audio Interface clocking API.*/
+int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+       unsigned int freq, int dir);
+
+int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
+       int div_id, int div);
+
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
+       int pll_id, unsigned int freq_in, unsigned int freq_out);
+
+/* Digital Audio interface formatting */
+int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
+
+int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
+       unsigned int mask, int slots);
+
+int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
+
+/* Digital Audio Interface mute */
+int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
+
+/*
+ * Digital Audio Interface.
+ *
+ * Describes the Digital Audio Interface in terms of it's ALSA, DAI and AC97
+ * operations an capabilities. Codec and platfom drivers will register a this
+ * structure for every DAI they have.
+ *
+ * This structure covers the clocking, formating and ALSA operations for each
+ * interface a
+ */
+struct snd_soc_dai_ops {
+       /*
+        * DAI clocking configuration, all optional.
+        * Called by soc_card drivers, normally in their hw_params.
+        */
+       int (*set_sysclk)(struct snd_soc_dai *dai,
+               int clk_id, unsigned int freq, int dir);
+       int (*set_pll)(struct snd_soc_dai *dai,
+               int pll_id, unsigned int freq_in, unsigned int freq_out);
+       int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
+
+       /*
+        * DAI format configuration
+        * Called by soc_card drivers, normally in their hw_params.
+        */
+       int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
+       int (*set_tdm_slot)(struct snd_soc_dai *dai,
+               unsigned int mask, int slots);
+       int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
+
+       /*
+        * DAI digital mute - optional.
+        * Called by soc-core to minimise any pops.
+        */
+       int (*digital_mute)(struct snd_soc_dai *dai, int mute);
+
+       /*
+        * ALSA PCM audio operations - all optional.
+        * Called by soc-core during audio PCM operations.
+        */
+       int (*startup)(struct snd_pcm_substream *,
+               struct snd_soc_dai *);
+       void (*shutdown)(struct snd_pcm_substream *,
+               struct snd_soc_dai *);
+       int (*hw_params)(struct snd_pcm_substream *,
+               struct snd_pcm_hw_params *, struct snd_soc_dai *);
+       int (*hw_free)(struct snd_pcm_substream *,
+               struct snd_soc_dai *);
+       int (*prepare)(struct snd_pcm_substream *,
+               struct snd_soc_dai *);
+       int (*trigger)(struct snd_pcm_substream *, int,
+               struct snd_soc_dai *);
+};
+
+/*
+ * Digital Audio Interface runtime data.
+ *
+ * Holds runtime data for a DAI.
+ */
+struct snd_soc_dai {
+       /* DAI description */
+       char *name;
+       unsigned int id;
+       int ac97_control;
+
+       struct device *dev;
+
+       /* DAI callbacks */
+       int (*probe)(struct platform_device *pdev,
+                    struct snd_soc_dai *dai);
+       void (*remove)(struct platform_device *pdev,
+                      struct snd_soc_dai *dai);
+       int (*suspend)(struct snd_soc_dai *dai);
+       int (*resume)(struct snd_soc_dai *dai);
+
+       /* ops */
+       struct snd_soc_dai_ops ops;
+
+       /* DAI capabilities */
+       struct snd_soc_pcm_stream capture;
+       struct snd_soc_pcm_stream playback;
+
+       /* DAI runtime info */
+       struct snd_pcm_runtime *runtime;
+       struct snd_soc_codec *codec;
+       unsigned int active;
+       unsigned char pop_wait:1;
+       void *dma_data;
+
+       /* DAI private data */
+       void *private_data;
+
+       /* parent codec/platform */
+       union {
+               struct snd_soc_codec *codec;
+               struct snd_soc_platform *platform;
+       };
+
+       struct list_head list;
+};
+
+#endif
index ca699a3..7ee2f70 100644 (file)
@@ -221,8 +221,6 @@ int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
        int num);
 
 /* dapm path setup */
-int  __deprecated snd_soc_dapm_connect_input(struct snd_soc_codec *codec,
-       const char *sink_name, const char *control_name, const char *src_name);
 int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec);
 void snd_soc_dapm_free(struct snd_soc_device *socdev);
 int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
index 5e01898..f86e455 100644 (file)
@@ -21,8 +21,6 @@
 #include <sound/control.h>
 #include <sound/ac97_codec.h>
 
-#define SND_SOC_VERSION "0.13.2"
-
 /*
  * Convenience kcontrol builders
  */
@@ -145,105 +143,31 @@ enum snd_soc_bias_level {
        SND_SOC_BIAS_OFF,
 };
 
-/*
- * Digital Audio Interface (DAI) types
- */
-#define SND_SOC_DAI_AC97       0x1
-#define SND_SOC_DAI_I2S                0x2
-#define SND_SOC_DAI_PCM                0x4
-#define SND_SOC_DAI_AC97_BUS   0x8     /* for custom i.e. non ac97_codec.c */
-
-/*
- * DAI hardware audio formats
- */
-#define SND_SOC_DAIFMT_I2S             0       /* I2S mode */
-#define SND_SOC_DAIFMT_RIGHT_J 1       /* Right justified mode */
-#define SND_SOC_DAIFMT_LEFT_J  2       /* Left Justified mode */
-#define SND_SOC_DAIFMT_DSP_A   3       /* L data msb after FRM or LRC */
-#define SND_SOC_DAIFMT_DSP_B   4       /* L data msb during FRM or LRC */
-#define SND_SOC_DAIFMT_AC97            5       /* AC97 */
-
-#define SND_SOC_DAIFMT_MSB     SND_SOC_DAIFMT_LEFT_J
-#define SND_SOC_DAIFMT_LSB     SND_SOC_DAIFMT_RIGHT_J
-
-/*
- * DAI Gating
- */
-#define SND_SOC_DAIFMT_CONT                    (0 << 4)        /* continuous clock */
-#define SND_SOC_DAIFMT_GATED           (1 << 4)        /* clock is gated when not Tx/Rx */
-
-/*
- * DAI Sync
- * Synchronous LR (Left Right) clocks and Frame signals.
- */
-#define SND_SOC_DAIFMT_SYNC            (0 << 5)        /* Tx FRM = Rx FRM */
-#define SND_SOC_DAIFMT_ASYNC           (1 << 5)        /* Tx FRM ~ Rx FRM */
-
-/*
- * TDM
- */
-#define SND_SOC_DAIFMT_TDM             (1 << 6)
-
-/*
- * DAI hardware signal inversions
- */
-#define SND_SOC_DAIFMT_NB_NF           (0 << 8)        /* normal bclk + frm */
-#define SND_SOC_DAIFMT_NB_IF           (1 << 8)        /* normal bclk + inv frm */
-#define SND_SOC_DAIFMT_IB_NF           (2 << 8)        /* invert bclk + nor frm */
-#define SND_SOC_DAIFMT_IB_IF           (3 << 8)        /* invert bclk + frm */
-
-/*
- * DAI hardware clock masters
- * This is wrt the codec, the inverse is true for the interface
- * i.e. if the codec is clk and frm master then the interface is
- * clk and frame slave.
- */
-#define SND_SOC_DAIFMT_CBM_CFM (0 << 12) /* codec clk & frm master */
-#define SND_SOC_DAIFMT_CBS_CFM (1 << 12) /* codec clk slave & frm master */
-#define SND_SOC_DAIFMT_CBM_CFS (2 << 12) /* codec clk master & frame slave */
-#define SND_SOC_DAIFMT_CBS_CFS (3 << 12) /* codec clk & frm slave */
-
-#define SND_SOC_DAIFMT_FORMAT_MASK             0x000f
-#define SND_SOC_DAIFMT_CLOCK_MASK              0x00f0
-#define SND_SOC_DAIFMT_INV_MASK                        0x0f00
-#define SND_SOC_DAIFMT_MASTER_MASK             0xf000
-
-
-/*
- * Master Clock Directions
- */
-#define SND_SOC_CLOCK_IN       0
-#define SND_SOC_CLOCK_OUT      1
-
-/*
- * AC97 codec ID's bitmask
- */
-#define SND_SOC_DAI_AC97_ID0   (1 << 0)
-#define SND_SOC_DAI_AC97_ID1   (1 << 1)
-#define SND_SOC_DAI_AC97_ID2   (1 << 2)
-#define SND_SOC_DAI_AC97_ID3   (1 << 3)
-
 struct snd_soc_device;
 struct snd_soc_pcm_stream;
 struct snd_soc_ops;
 struct snd_soc_dai_mode;
 struct snd_soc_pcm_runtime;
 struct snd_soc_dai;
+struct snd_soc_platform;
 struct snd_soc_codec;
-struct snd_soc_machine_config;
 struct soc_enum;
 struct snd_soc_ac97_ops;
-struct snd_soc_clock_info;
 
 typedef int (*hw_write_t)(void *,const char* ,int);
 typedef int (*hw_read_t)(void *,char* ,int);
 
 extern struct snd_ac97_bus_ops soc_ac97_ops;
 
+int snd_soc_register_platform(struct snd_soc_platform *platform);
+void snd_soc_unregister_platform(struct snd_soc_platform *platform);
+int snd_soc_register_codec(struct snd_soc_codec *codec);
+void snd_soc_unregister_codec(struct snd_soc_codec *codec);
+
 /* pcm <-> DAI connect */
 void snd_soc_free_pcms(struct snd_soc_device *socdev);
 int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid);
-int snd_soc_register_card(struct snd_soc_device *socdev);
+int snd_soc_init_card(struct snd_soc_device *socdev);
 
 /* set runtime hw params */
 int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
@@ -263,27 +187,6 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
        struct snd_ac97_bus_ops *ops, int num);
 void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
 
-/* Digital Audio Interface clocking API.*/
-int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
-       unsigned int freq, int dir);
-
-int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
-       int div_id, int div);
-
-int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
-       int pll_id, unsigned int freq_in, unsigned int freq_out);
-
-/* Digital Audio interface formatting */
-int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
-
-int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
-       unsigned int mask, int slots);
-
-int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
-
-/* Digital Audio Interface mute */
-int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
-
 /*
  *Controls
  */
@@ -341,66 +244,14 @@ struct snd_soc_ops {
        int (*trigger)(struct snd_pcm_substream *, int);
 };
 
-/* ASoC DAI ops */
-struct snd_soc_dai_ops {
-       /* DAI clocking configuration */
-       int (*set_sysclk)(struct snd_soc_dai *dai,
-               int clk_id, unsigned int freq, int dir);
-       int (*set_pll)(struct snd_soc_dai *dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out);
-       int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
-
-       /* DAI format configuration */
-       int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
-       int (*set_tdm_slot)(struct snd_soc_dai *dai,
-               unsigned int mask, int slots);
-       int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
-
-       /* digital mute */
-       int (*digital_mute)(struct snd_soc_dai *dai, int mute);
-};
-
-/* SoC  DAI (Digital Audio Interface) */
-struct snd_soc_dai {
-       /* DAI description */
-       char *name;
-       unsigned int id;
-       unsigned char type;
-
-       /* DAI callbacks */
-       int (*probe)(struct platform_device *pdev,
-                    struct snd_soc_dai *dai);
-       void (*remove)(struct platform_device *pdev,
-                      struct snd_soc_dai *dai);
-       int (*suspend)(struct platform_device *pdev,
-               struct snd_soc_dai *dai);
-       int (*resume)(struct platform_device *pdev,
-               struct snd_soc_dai *dai);
-
-       /* ops */
-       struct snd_soc_ops ops;
-       struct snd_soc_dai_ops dai_ops;
-
-       /* DAI capabilities */
-       struct snd_soc_pcm_stream capture;
-       struct snd_soc_pcm_stream playback;
-
-       /* DAI runtime info */
-       struct snd_pcm_runtime *runtime;
-       struct snd_soc_codec *codec;
-       unsigned int active;
-       unsigned char pop_wait:1;
-       void *dma_data;
-
-       /* DAI private data */
-       void *private_data;
-};
-
 /* SoC Audio Codec */
 struct snd_soc_codec {
        char *name;
        struct module *owner;
        struct mutex mutex;
+       struct device *dev;
+
+       struct list_head list;
 
        /* callbacks */
        int (*set_bias_level)(struct snd_soc_codec *,
@@ -426,6 +277,7 @@ struct snd_soc_codec {
        short reg_cache_step;
 
        /* dapm */
+       u32 pop_time;
        struct list_head dapm_widgets;
        struct list_head dapm_paths;
        enum snd_soc_bias_level bias_level;
@@ -435,6 +287,11 @@ struct snd_soc_codec {
        /* codec DAI's */
        struct snd_soc_dai *dai;
        unsigned int num_dai;
+
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *debugfs_reg;
+       struct dentry *debugfs_pop_time;
+#endif
 };
 
 /* codec device */
@@ -448,13 +305,12 @@ struct snd_soc_codec_device {
 /* SoC platform interface */
 struct snd_soc_platform {
        char *name;
+       struct list_head list;
 
        int (*probe)(struct platform_device *pdev);
        int (*remove)(struct platform_device *pdev);
-       int (*suspend)(struct platform_device *pdev,
-               struct snd_soc_dai *dai);
-       int (*resume)(struct platform_device *pdev,
-               struct snd_soc_dai *dai);
+       int (*suspend)(struct snd_soc_dai *dai);
+       int (*resume)(struct snd_soc_dai *dai);
 
        /* pcm creation and destruction */
        int (*pcm_new)(struct snd_card *, struct snd_soc_dai *,
@@ -484,9 +340,14 @@ struct snd_soc_dai_link  {
        struct snd_pcm *pcm;
 };
 
-/* SoC machine */
-struct snd_soc_machine {
+/* SoC card */
+struct snd_soc_card {
        char *name;
+       struct device *dev;
+
+       struct list_head list;
+
+       int instantiated;
 
        int (*probe)(struct platform_device *pdev);
        int (*remove)(struct platform_device *pdev);
@@ -499,23 +360,26 @@ struct snd_soc_machine {
        int (*resume_post)(struct platform_device *pdev);
 
        /* callbacks */
-       int (*set_bias_level)(struct snd_soc_machine *,
+       int (*set_bias_level)(struct snd_soc_card *,
                              enum snd_soc_bias_level level);
 
        /* CPU <--> Codec DAI links  */
        struct snd_soc_dai_link *dai_link;
        int num_links;
+
+       struct snd_soc_device *socdev;
+
+       struct snd_soc_platform *platform;
+       struct delayed_work delayed_work;
+       struct work_struct deferred_resume_work;
 };
 
 /* SoC Device - the audio subsystem */
 struct snd_soc_device {
        struct device *dev;
-       struct snd_soc_machine *machine;
-       struct snd_soc_platform *platform;
+       struct snd_soc_card *card;
        struct snd_soc_codec *codec;
        struct snd_soc_codec_device *codec_dev;
-       struct delayed_work delayed_work;
-       struct work_struct deferred_resume_work;
        void *codec_data;
 };
 
@@ -542,4 +406,6 @@ struct soc_enum {
        void *dapm;
 };
 
+#include <sound/soc-dai.h>
+
 #endif
diff --git a/include/sound/uda134x.h b/include/sound/uda134x.h
new file mode 100644 (file)
index 0000000..475ef8b
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * uda134x.h  --  UDA134x ALSA SoC Codec driver
+ *
+ * Copyright 2007 Dension Audio Systems Ltd.
+ * Author: Zoltan Devai
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _UDA134X_H
+#define _UDA134X_H
+
+#include <sound/l3.h>
+
+struct uda134x_platform_data {
+       struct l3_pins l3;
+       void (*power) (int);
+       int model;
+#define UDA134X_UDA1340 1
+#define UDA134X_UDA1341 2
+#define UDA134X_UDA1344 3
+};
+
+#endif /* _UDA134X_H */
index 4aafeda..2b48237 100644 (file)
@@ -1,3 +1,3 @@
 /* include/version.h */
-#define CONFIG_SND_VERSION "1.0.18rc3"
+#define CONFIG_SND_VERSION "1.0.18a"
 #define CONFIG_SND_DATE ""
index 1a4bc6a..25144ab 100644 (file)
@@ -37,6 +37,7 @@ enum { LCDC_CLK_BUS, LCDC_CLK_PERIPHERAL, LCDC_CLK_EXTERNAL };
 struct sh_mobile_lcdc_sys_bus_cfg {
        unsigned long ldmt2r;
        unsigned long ldmt3r;
+       unsigned long deferred_io_msec;
 };
 
 struct sh_mobile_lcdc_sys_bus_ops {
index 7e117a2..db843bf 100644 (file)
@@ -669,6 +669,7 @@ asmlinkage void __init start_kernel(void)
                efi_enter_virtual_mode();
 #endif
        thread_info_cache_init();
+       cred_init();
        fork_init(num_physpages);
        proc_caches_init();
        buffer_init();
index 68eb857..d9393f8 100644 (file)
@@ -112,13 +112,14 @@ static inline struct mqueue_inode_info *MQUEUE_I(struct inode *inode)
 static struct inode *mqueue_get_inode(struct super_block *sb, int mode,
                                                        struct mq_attr *attr)
 {
+       struct user_struct *u = current_user();
        struct inode *inode;
 
        inode = new_inode(sb);
        if (inode) {
                inode->i_mode = mode;
-               inode->i_uid = current->fsuid;
-               inode->i_gid = current->fsgid;
+               inode->i_uid = current_fsuid();
+               inode->i_gid = current_fsgid();
                inode->i_blocks = 0;
                inode->i_mtime = inode->i_ctime = inode->i_atime =
                                CURRENT_TIME;
@@ -126,7 +127,6 @@ static struct inode *mqueue_get_inode(struct super_block *sb, int mode,
                if (S_ISREG(mode)) {
                        struct mqueue_inode_info *info;
                        struct task_struct *p = current;
-                       struct user_struct *u = p->user;
                        unsigned long mq_bytes, mq_msg_tblsz;
 
                        inode->i_fop = &mqueue_file_operations;
@@ -507,7 +507,7 @@ static void __do_notify(struct mqueue_inode_info *info)
                        sig_i.si_code = SI_MESGQ;
                        sig_i.si_value = info->notify.sigev_value;
                        sig_i.si_pid = task_tgid_vnr(current);
-                       sig_i.si_uid = current->uid;
+                       sig_i.si_uid = current_uid();
 
                        kill_pid_info(info->notify.sigev_signo,
                                      &sig_i, info->notify_owner);
@@ -594,6 +594,7 @@ static int mq_attr_ok(struct mq_attr *attr)
 static struct file *do_create(struct dentry *dir, struct dentry *dentry,
                        int oflag, mode_t mode, struct mq_attr __user *u_attr)
 {
+       const struct cred *cred = current_cred();
        struct mq_attr attr;
        struct file *result;
        int ret;
@@ -618,7 +619,7 @@ static struct file *do_create(struct dentry *dir, struct dentry *dentry,
        if (ret)
                goto out_drop_write;
 
-       result = dentry_open(dentry, mqueue_mnt, oflag);
+       result = dentry_open(dentry, mqueue_mnt, oflag, cred);
        /*
         * dentry_open() took a persistent mnt_want_write(),
         * so we can now drop this one.
@@ -637,8 +638,10 @@ out:
 /* Opens existing queue */
 static struct file *do_open(struct dentry *dentry, int oflag)
 {
-static int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE,
-                                       MAY_READ | MAY_WRITE };
+       const struct cred *cred = current_cred();
+
+       static const int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE,
+                                                 MAY_READ | MAY_WRITE };
 
        if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) {
                dput(dentry);
@@ -652,7 +655,7 @@ static int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE,
                return ERR_PTR(-EACCES);
        }
 
-       return dentry_open(dentry, mqueue_mnt, oflag);
+       return dentry_open(dentry, mqueue_mnt, oflag, cred);
 }
 
 asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode,
index 867e5d6..38a0557 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -366,7 +366,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
        if (shmflg & SHM_HUGETLB) {
                /* hugetlb_file_setup takes care of mlock user accounting */
                file = hugetlb_file_setup(name, size);
-               shp->mlock_user = current->user;
+               shp->mlock_user = current_user();
        } else {
                int acctflag = VM_ACCOUNT;
                /*
@@ -752,9 +752,10 @@ asmlinkage long sys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf)
                        goto out_unlock;
 
                if (!capable(CAP_IPC_LOCK)) {
+                       uid_t euid = current_euid();
                        err = -EPERM;
-                       if (current->euid != shp->shm_perm.uid &&
-                           current->euid != shp->shm_perm.cuid)
+                       if (euid != shp->shm_perm.uid &&
+                           euid != shp->shm_perm.cuid)
                                goto out_unlock;
                        if (cmd == SHM_LOCK &&
                            !current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur)
@@ -766,7 +767,7 @@ asmlinkage long sys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf)
                        goto out_unlock;
                
                if(cmd==SHM_LOCK) {
-                       struct user_struct * user = current->user;
+                       struct user_struct *user = current_user();
                        if (!is_file_hugepages(shp->shm_file)) {
                                err = shmem_lock(shp->shm_file, 1, user);
                                if (!err && !(shp->shm_perm.mode & SHM_LOCKED)){
index 361fd1c..5a1808c 100644 (file)
@@ -258,6 +258,8 @@ int ipc_get_maxid(struct ipc_ids *ids)
  
 int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
 {
+       uid_t euid;
+       gid_t egid;
        int id, err;
 
        if (size > IPCMNI)
@@ -280,8 +282,9 @@ int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
 
        ids->in_use++;
 
-       new->cuid = new->uid = current->euid;
-       new->gid = new->cgid = current->egid;
+       current_euid_egid(&euid, &egid);
+       new->cuid = new->uid = euid;
+       new->gid = new->cgid = egid;
 
        new->seq = ids->seq++;
        if(ids->seq > ids->seq_max)
@@ -620,13 +623,15 @@ void ipc_rcu_putref(void *ptr)
  
 int ipcperms (struct kern_ipc_perm *ipcp, short flag)
 {      /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
+       uid_t euid = current_euid();
        int requested_mode, granted_mode, err;
 
        if (unlikely((err = audit_ipc_obj(ipcp))))
                return err;
        requested_mode = (flag >> 6) | (flag >> 3) | flag;
        granted_mode = ipcp->mode;
-       if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
+       if (euid == ipcp->cuid ||
+           euid == ipcp->uid)
                granted_mode >>= 6;
        else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid))
                granted_mode >>= 3;
@@ -788,6 +793,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd,
                                      struct ipc64_perm *perm, int extra_perm)
 {
        struct kern_ipc_perm *ipcp;
+       uid_t euid;
        int err;
 
        down_write(&ids->rw_mutex);
@@ -807,8 +813,10 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd,
                if (err)
                        goto out_unlock;
        }
-       if (current->euid == ipcp->cuid ||
-           current->euid == ipcp->uid || capable(CAP_SYS_ADMIN))
+
+       euid = current_euid();
+       if (euid == ipcp->cuid ||
+           euid == ipcp->uid  || capable(CAP_SYS_ADMIN))
                return ipcp;
 
        err = -EPERM;
index 19fad00..b1e6b66 100644 (file)
@@ -9,7 +9,7 @@ obj-y     = sched.o fork.o exec_domain.o panic.o printk.o \
            rcupdate.o extable.o params.o posix-timers.o \
            kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
            hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
-           notifier.o ksysfs.o pm_qos_params.o sched_clock.o
+           notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o
 
 ifdef CONFIG_FUNCTION_TRACER
 # Do not trace debug files and internal ftrace files
index f6006a6..d57b7cb 100644 (file)
@@ -530,15 +530,14 @@ static void do_acct_process(struct bsd_acct_struct *acct,
        do_div(elapsed, AHZ);
        ac.ac_btime = get_seconds() - elapsed;
        /* we really need to bite the bullet and change layout */
-       ac.ac_uid = current->uid;
-       ac.ac_gid = current->gid;
+       current_uid_gid(&ac.ac_uid, &ac.ac_gid);
 #if ACCT_VERSION==2
        ac.ac_ahz = AHZ;
 #endif
 #if ACCT_VERSION==1 || ACCT_VERSION==2
        /* backward-compatible 16 bit fields */
-       ac.ac_uid16 = current->uid;
-       ac.ac_gid16 = current->gid;
+       ac.ac_uid16 = ac.ac_uid;
+       ac.ac_gid16 = ac.ac_gid;
 #endif
 #if ACCT_VERSION==3
        ac.ac_pid = task_tgid_nr_ns(current, ns);
index 2a3f0af..4819f37 100644 (file)
@@ -65,6 +65,7 @@
 #include <linux/highmem.h>
 #include <linux/syscalls.h>
 #include <linux/inotify.h>
+#include <linux/capability.h>
 
 #include "audit.h"
 
@@ -84,6 +85,15 @@ int audit_n_rules;
 /* determines whether we collect data for signals sent */
 int audit_signals;
 
+struct audit_cap_data {
+       kernel_cap_t            permitted;
+       kernel_cap_t            inheritable;
+       union {
+               unsigned int    fE;             /* effective bit of a file capability */
+               kernel_cap_t    effective;      /* effective set of a process */
+       };
+};
+
 /* When fs/namei.c:getname() is called, we store the pointer in name and
  * we don't let putname() free it (instead we free all of the saved
  * pointers at syscall exit time).
@@ -100,6 +110,8 @@ struct audit_names {
        gid_t           gid;
        dev_t           rdev;
        u32             osid;
+       struct audit_cap_data fcap;
+       unsigned int    fcap_ver;
 };
 
 struct audit_aux_data {
@@ -184,6 +196,20 @@ struct audit_aux_data_pids {
        int                     pid_count;
 };
 
+struct audit_aux_data_bprm_fcaps {
+       struct audit_aux_data   d;
+       struct audit_cap_data   fcap;
+       unsigned int            fcap_ver;
+       struct audit_cap_data   old_pcap;
+       struct audit_cap_data   new_pcap;
+};
+
+struct audit_aux_data_capset {
+       struct audit_aux_data   d;
+       pid_t                   pid;
+       struct audit_cap_data   cap;
+};
+
 struct audit_tree_refs {
        struct audit_tree_refs *next;
        struct audit_chunk *c[31];
@@ -421,6 +447,7 @@ static int audit_filter_rules(struct task_struct *tsk,
                              struct audit_names *name,
                              enum audit_state *state)
 {
+       const struct cred *cred = get_task_cred(tsk);
        int i, j, need_sid = 1;
        u32 sid;
 
@@ -440,28 +467,28 @@ static int audit_filter_rules(struct task_struct *tsk,
                        }
                        break;
                case AUDIT_UID:
-                       result = audit_comparator(tsk->uid, f->op, f->val);
+                       result = audit_comparator(cred->uid, f->op, f->val);
                        break;
                case AUDIT_EUID:
-                       result = audit_comparator(tsk->euid, f->op, f->val);
+                       result = audit_comparator(cred->euid, f->op, f->val);
                        break;
                case AUDIT_SUID:
-                       result = audit_comparator(tsk->suid, f->op, f->val);
+                       result = audit_comparator(cred->suid, f->op, f->val);
                        break;
                case AUDIT_FSUID:
-                       result = audit_comparator(tsk->fsuid, f->op, f->val);
+                       result = audit_comparator(cred->fsuid, f->op, f->val);
                        break;
                case AUDIT_GID:
-                       result = audit_comparator(tsk->gid, f->op, f->val);
+                       result = audit_comparator(cred->gid, f->op, f->val);
                        break;
                case AUDIT_EGID:
-                       result = audit_comparator(tsk->egid, f->op, f->val);
+                       result = audit_comparator(cred->egid, f->op, f->val);
                        break;
                case AUDIT_SGID:
-                       result = audit_comparator(tsk->sgid, f->op, f->val);
+                       result = audit_comparator(cred->sgid, f->op, f->val);
                        break;
                case AUDIT_FSGID:
-                       result = audit_comparator(tsk->fsgid, f->op, f->val);
+                       result = audit_comparator(cred->fsgid, f->op, f->val);
                        break;
                case AUDIT_PERS:
                        result = audit_comparator(tsk->personality, f->op, f->val);
@@ -615,8 +642,10 @@ static int audit_filter_rules(struct task_struct *tsk,
                        break;
                }
 
-               if (!result)
+               if (!result) {
+                       put_cred(cred);
                        return 0;
+               }
        }
        if (rule->filterkey && ctx)
                ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC);
@@ -624,6 +653,7 @@ static int audit_filter_rules(struct task_struct *tsk,
        case AUDIT_NEVER:    *state = AUDIT_DISABLED;       break;
        case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
        }
+       put_cred(cred);
        return 1;
 }
 
@@ -1171,8 +1201,38 @@ static void audit_log_execve_info(struct audit_context *context,
        kfree(buf);
 }
 
+static void audit_log_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *cap)
+{
+       int i;
+
+       audit_log_format(ab, " %s=", prefix);
+       CAP_FOR_EACH_U32(i) {
+               audit_log_format(ab, "%08x", cap->cap[(_KERNEL_CAPABILITY_U32S-1) - i]);
+       }
+}
+
+static void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name)
+{
+       kernel_cap_t *perm = &name->fcap.permitted;
+       kernel_cap_t *inh = &name->fcap.inheritable;
+       int log = 0;
+
+       if (!cap_isclear(*perm)) {
+               audit_log_cap(ab, "cap_fp", perm);
+               log = 1;
+       }
+       if (!cap_isclear(*inh)) {
+               audit_log_cap(ab, "cap_fi", inh);
+               log = 1;
+       }
+
+       if (log)
+               audit_log_format(ab, " cap_fe=%d cap_fver=%x", name->fcap.fE, name->fcap_ver);
+}
+
 static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
 {
+       const struct cred *cred;
        int i, call_panic = 0;
        struct audit_buffer *ab;
        struct audit_aux_data *aux;
@@ -1182,14 +1242,15 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
        context->pid = tsk->pid;
        if (!context->ppid)
                context->ppid = sys_getppid();
-       context->uid = tsk->uid;
-       context->gid = tsk->gid;
-       context->euid = tsk->euid;
-       context->suid = tsk->suid;
-       context->fsuid = tsk->fsuid;
-       context->egid = tsk->egid;
-       context->sgid = tsk->sgid;
-       context->fsgid = tsk->fsgid;
+       cred = current_cred();
+       context->uid   = cred->uid;
+       context->gid   = cred->gid;
+       context->euid  = cred->euid;
+       context->suid  = cred->suid;
+       context->fsuid = cred->fsuid;
+       context->egid  = cred->egid;
+       context->sgid  = cred->sgid;
+       context->fsgid = cred->fsgid;
        context->personality = tsk->personality;
 
        ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL);
@@ -1334,6 +1395,28 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
                        audit_log_format(ab, "fd0=%d fd1=%d", axs->fd[0], axs->fd[1]);
                        break; }
 
+               case AUDIT_BPRM_FCAPS: {
+                       struct audit_aux_data_bprm_fcaps *axs = (void *)aux;
+                       audit_log_format(ab, "fver=%x", axs->fcap_ver);
+                       audit_log_cap(ab, "fp", &axs->fcap.permitted);
+                       audit_log_cap(ab, "fi", &axs->fcap.inheritable);
+                       audit_log_format(ab, " fe=%d", axs->fcap.fE);
+                       audit_log_cap(ab, "old_pp", &axs->old_pcap.permitted);
+                       audit_log_cap(ab, "old_pi", &axs->old_pcap.inheritable);
+                       audit_log_cap(ab, "old_pe", &axs->old_pcap.effective);
+                       audit_log_cap(ab, "new_pp", &axs->new_pcap.permitted);
+                       audit_log_cap(ab, "new_pi", &axs->new_pcap.inheritable);
+                       audit_log_cap(ab, "new_pe", &axs->new_pcap.effective);
+                       break; }
+
+               case AUDIT_CAPSET: {
+                       struct audit_aux_data_capset *axs = (void *)aux;
+                       audit_log_format(ab, "pid=%d", axs->pid);
+                       audit_log_cap(ab, "cap_pi", &axs->cap.inheritable);
+                       audit_log_cap(ab, "cap_pp", &axs->cap.permitted);
+                       audit_log_cap(ab, "cap_pe", &axs->cap.effective);
+                       break; }
+
                }
                audit_log_end(ab);
        }
@@ -1421,6 +1504,8 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
                        }
                }
 
+               audit_log_fcaps(ab, n);
+
                audit_log_end(ab);
        }
 
@@ -1802,8 +1887,36 @@ static int audit_inc_name_count(struct audit_context *context,
        return 0;
 }
 
+
+static inline int audit_copy_fcaps(struct audit_names *name, const struct dentry *dentry)
+{
+       struct cpu_vfs_cap_data caps;
+       int rc;
+
+       memset(&name->fcap.permitted, 0, sizeof(kernel_cap_t));
+       memset(&name->fcap.inheritable, 0, sizeof(kernel_cap_t));
+       name->fcap.fE = 0;
+       name->fcap_ver = 0;
+
+       if (!dentry)
+               return 0;
+
+       rc = get_vfs_caps_from_disk(dentry, &caps);
+       if (rc)
+               return rc;
+
+       name->fcap.permitted = caps.permitted;
+       name->fcap.inheritable = caps.inheritable;
+       name->fcap.fE = !!(caps.magic_etc & VFS_CAP_FLAGS_EFFECTIVE);
+       name->fcap_ver = (caps.magic_etc & VFS_CAP_REVISION_MASK) >> VFS_CAP_REVISION_SHIFT;
+
+       return 0;
+}
+
+
 /* Copy inode data into an audit_names. */
-static void audit_copy_inode(struct audit_names *name, const struct inode *inode)
+static void audit_copy_inode(struct audit_names *name, const struct dentry *dentry,
+                            const struct inode *inode)
 {
        name->ino   = inode->i_ino;
        name->dev   = inode->i_sb->s_dev;
@@ -1812,6 +1925,7 @@ static void audit_copy_inode(struct audit_names *name, const struct inode *inode
        name->gid   = inode->i_gid;
        name->rdev  = inode->i_rdev;
        security_inode_getsecid(inode, &name->osid);
+       audit_copy_fcaps(name, dentry);
 }
 
 /**
@@ -1846,7 +1960,7 @@ void __audit_inode(const char *name, const struct dentry *dentry)
                context->names[idx].name = NULL;
        }
        handle_path(dentry);
-       audit_copy_inode(&context->names[idx], inode);
+       audit_copy_inode(&context->names[idx], dentry, inode);
 }
 
 /**
@@ -1907,7 +2021,7 @@ void __audit_inode_child(const char *dname, const struct dentry *dentry,
                if (!strcmp(dname, n->name) ||
                     !audit_compare_dname_path(dname, n->name, &dirlen)) {
                        if (inode)
-                               audit_copy_inode(n, inode);
+                               audit_copy_inode(n, NULL, inode);
                        else
                                n->ino = (unsigned long)-1;
                        found_child = n->name;
@@ -1921,7 +2035,7 @@ add_names:
                        return;
                idx = context->name_count - 1;
                context->names[idx].name = NULL;
-               audit_copy_inode(&context->names[idx], parent);
+               audit_copy_inode(&context->names[idx], NULL, parent);
        }
 
        if (!found_child) {
@@ -1942,7 +2056,7 @@ add_names:
                }
 
                if (inode)
-                       audit_copy_inode(&context->names[idx], inode);
+                       audit_copy_inode(&context->names[idx], NULL, inode);
                else
                        context->names[idx].ino = (unsigned long)-1;
        }
@@ -1996,7 +2110,7 @@ int audit_set_loginuid(struct task_struct *task, uid_t loginuid)
                        audit_log_format(ab, "login pid=%d uid=%u "
                                "old auid=%u new auid=%u"
                                " old ses=%u new ses=%u",
-                               task->pid, task->uid,
+                               task->pid, task_uid(task),
                                task->loginuid, loginuid,
                                task->sessionid, sessionid);
                        audit_log_end(ab);
@@ -2379,7 +2493,7 @@ void __audit_ptrace(struct task_struct *t)
 
        context->target_pid = t->pid;
        context->target_auid = audit_get_loginuid(t);
-       context->target_uid = t->uid;
+       context->target_uid = task_uid(t);
        context->target_sessionid = audit_get_sessionid(t);
        security_task_getsecid(t, &context->target_sid);
        memcpy(context->target_comm, t->comm, TASK_COMM_LEN);
@@ -2398,6 +2512,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
        struct audit_aux_data_pids *axp;
        struct task_struct *tsk = current;
        struct audit_context *ctx = tsk->audit_context;
+       uid_t uid = current_uid(), t_uid = task_uid(t);
 
        if (audit_pid && t->tgid == audit_pid) {
                if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1 || sig == SIGUSR2) {
@@ -2405,7 +2520,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
                        if (tsk->loginuid != -1)
                                audit_sig_uid = tsk->loginuid;
                        else
-                               audit_sig_uid = tsk->uid;
+                               audit_sig_uid = uid;
                        security_task_getsecid(tsk, &audit_sig_sid);
                }
                if (!audit_signals || audit_dummy_context())
@@ -2417,7 +2532,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
        if (!ctx->target_pid) {
                ctx->target_pid = t->tgid;
                ctx->target_auid = audit_get_loginuid(t);
-               ctx->target_uid = t->uid;
+               ctx->target_uid = t_uid;
                ctx->target_sessionid = audit_get_sessionid(t);
                security_task_getsecid(t, &ctx->target_sid);
                memcpy(ctx->target_comm, t->comm, TASK_COMM_LEN);
@@ -2438,7 +2553,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
 
        axp->target_pid[axp->pid_count] = t->tgid;
        axp->target_auid[axp->pid_count] = audit_get_loginuid(t);
-       axp->target_uid[axp->pid_count] = t->uid;
+       axp->target_uid[axp->pid_count] = t_uid;
        axp->target_sessionid[axp->pid_count] = audit_get_sessionid(t);
        security_task_getsecid(t, &axp->target_sid[axp->pid_count]);
        memcpy(axp->target_comm[axp->pid_count], t->comm, TASK_COMM_LEN);
@@ -2447,6 +2562,86 @@ int __audit_signal_info(int sig, struct task_struct *t)
        return 0;
 }
 
+/**
+ * __audit_log_bprm_fcaps - store information about a loading bprm and relevant fcaps
+ * @bprm: pointer to the bprm being processed
+ * @new: the proposed new credentials
+ * @old: the old credentials
+ *
+ * Simply check if the proc already has the caps given by the file and if not
+ * store the priv escalation info for later auditing at the end of the syscall
+ *
+ * -Eric
+ */
+int __audit_log_bprm_fcaps(struct linux_binprm *bprm,
+                          const struct cred *new, const struct cred *old)
+{
+       struct audit_aux_data_bprm_fcaps *ax;
+       struct audit_context *context = current->audit_context;
+       struct cpu_vfs_cap_data vcaps;
+       struct dentry *dentry;
+
+       ax = kmalloc(sizeof(*ax), GFP_KERNEL);
+       if (!ax)
+               return -ENOMEM;
+
+       ax->d.type = AUDIT_BPRM_FCAPS;
+       ax->d.next = context->aux;
+       context->aux = (void *)ax;
+
+       dentry = dget(bprm->file->f_dentry);
+       get_vfs_caps_from_disk(dentry, &vcaps);
+       dput(dentry);
+
+       ax->fcap.permitted = vcaps.permitted;
+       ax->fcap.inheritable = vcaps.inheritable;
+       ax->fcap.fE = !!(vcaps.magic_etc & VFS_CAP_FLAGS_EFFECTIVE);
+       ax->fcap_ver = (vcaps.magic_etc & VFS_CAP_REVISION_MASK) >> VFS_CAP_REVISION_SHIFT;
+
+       ax->old_pcap.permitted   = old->cap_permitted;
+       ax->old_pcap.inheritable = old->cap_inheritable;
+       ax->old_pcap.effective   = old->cap_effective;
+
+       ax->new_pcap.permitted   = new->cap_permitted;
+       ax->new_pcap.inheritable = new->cap_inheritable;
+       ax->new_pcap.effective   = new->cap_effective;
+       return 0;
+}
+
+/**
+ * __audit_log_capset - store information about the arguments to the capset syscall
+ * @pid: target pid of the capset call
+ * @new: the new credentials
+ * @old: the old (current) credentials
+ *
+ * Record the aguments userspace sent to sys_capset for later printing by the
+ * audit system if applicable
+ */
+int __audit_log_capset(pid_t pid,
+                      const struct cred *new, const struct cred *old)
+{
+       struct audit_aux_data_capset *ax;
+       struct audit_context *context = current->audit_context;
+
+       if (likely(!audit_enabled || !context || context->dummy))
+               return 0;
+
+       ax = kmalloc(sizeof(*ax), GFP_KERNEL);
+       if (!ax)
+               return -ENOMEM;
+
+       ax->d.type = AUDIT_CAPSET;
+       ax->d.next = context->aux;
+       context->aux = (void *)ax;
+
+       ax->pid = pid;
+       ax->cap.effective   = new->cap_effective;
+       ax->cap.inheritable = new->cap_effective;
+       ax->cap.permitted   = new->cap_permitted;
+
+       return 0;
+}
+
 /**
  * audit_core_dumps - record information about processes that end abnormally
  * @signr: signal value
@@ -2458,7 +2653,8 @@ void audit_core_dumps(long signr)
 {
        struct audit_buffer *ab;
        u32 sid;
-       uid_t auid = audit_get_loginuid(current);
+       uid_t auid = audit_get_loginuid(current), uid;
+       gid_t gid;
        unsigned int sessionid = audit_get_sessionid(current);
 
        if (!audit_enabled)
@@ -2468,8 +2664,9 @@ void audit_core_dumps(long signr)
                return;
 
        ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND);
+       current_uid_gid(&uid, &gid);
        audit_log_format(ab, "auid=%u uid=%u gid=%u ses=%u",
-                       auid, current->uid, current->gid, sessionid);
+                        auid, uid, gid, sessionid);
        security_task_getsecid(current, &sid);
        if (sid) {
                char *ctx = NULL;
index 33e51e7..36b4b4d 100644 (file)
@@ -7,6 +7,7 @@
  * 30 May 2002:        Cleanup, Robert M. Love <rml@tech9.net>
  */
 
+#include <linux/audit.h>
 #include <linux/capability.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/syscalls.h>
 #include <linux/pid_namespace.h>
 #include <asm/uaccess.h>
-
-/*
- * This lock protects task->cap_* for all tasks including current.
- * Locking rule: acquire this prior to tasklist_lock.
- */
-static DEFINE_SPINLOCK(task_capability_lock);
+#include "cred-internals.h"
 
 /*
  * Leveraged for setting/resetting capabilities
@@ -33,6 +29,17 @@ EXPORT_SYMBOL(__cap_empty_set);
 EXPORT_SYMBOL(__cap_full_set);
 EXPORT_SYMBOL(__cap_init_eff_set);
 
+#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
+int file_caps_enabled = 1;
+
+static int __init file_caps_disable(char *str)
+{
+       file_caps_enabled = 0;
+       return 1;
+}
+__setup("no_file_caps", file_caps_disable);
+#endif
+
 /*
  * More recent versions of libcap are available from:
  *
@@ -115,167 +122,12 @@ static int cap_validate_magic(cap_user_header_t header, unsigned *tocopy)
        return 0;
 }
 
-#ifndef CONFIG_SECURITY_FILE_CAPABILITIES
-
-/*
- * Without filesystem capability support, we nominally support one process
- * setting the capabilities of another
- */
-static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
-                                    kernel_cap_t *pIp, kernel_cap_t *pPp)
-{
-       struct task_struct *target;
-       int ret;
-
-       spin_lock(&task_capability_lock);
-       read_lock(&tasklist_lock);
-
-       if (pid && pid != task_pid_vnr(current)) {
-               target = find_task_by_vpid(pid);
-               if (!target) {
-                       ret = -ESRCH;
-                       goto out;
-               }
-       } else
-               target = current;
-
-       ret = security_capget(target, pEp, pIp, pPp);
-
-out:
-       read_unlock(&tasklist_lock);
-       spin_unlock(&task_capability_lock);
-
-       return ret;
-}
-
-/*
- * cap_set_pg - set capabilities for all processes in a given process
- * group.  We call this holding task_capability_lock and tasklist_lock.
- */
-static inline int cap_set_pg(int pgrp_nr, kernel_cap_t *effective,
-                            kernel_cap_t *inheritable,
-                            kernel_cap_t *permitted)
-{
-       struct task_struct *g, *target;
-       int ret = -EPERM;
-       int found = 0;
-       struct pid *pgrp;
-
-       spin_lock(&task_capability_lock);
-       read_lock(&tasklist_lock);
-
-       pgrp = find_vpid(pgrp_nr);
-       do_each_pid_task(pgrp, PIDTYPE_PGID, g) {
-               target = g;
-               while_each_thread(g, target) {
-                       if (!security_capset_check(target, effective,
-                                                  inheritable, permitted)) {
-                               security_capset_set(target, effective,
-                                                   inheritable, permitted);
-                               ret = 0;
-                       }
-                       found = 1;
-               }
-       } while_each_pid_task(pgrp, PIDTYPE_PGID, g);
-
-       read_unlock(&tasklist_lock);
-       spin_unlock(&task_capability_lock);
-
-       if (!found)
-               ret = 0;
-       return ret;
-}
-
-/*
- * cap_set_all - set capabilities for all processes other than init
- * and self.  We call this holding task_capability_lock and tasklist_lock.
- */
-static inline int cap_set_all(kernel_cap_t *effective,
-                             kernel_cap_t *inheritable,
-                             kernel_cap_t *permitted)
-{
-       struct task_struct *g, *target;
-       int ret = -EPERM;
-       int found = 0;
-
-       spin_lock(&task_capability_lock);
-       read_lock(&tasklist_lock);
-
-       do_each_thread(g, target) {
-               if (target == current
-                   || is_container_init(target->group_leader))
-                       continue;
-               found = 1;
-               if (security_capset_check(target, effective, inheritable,
-                                         permitted))
-                       continue;
-               ret = 0;
-               security_capset_set(target, effective, inheritable, permitted);
-       } while_each_thread(g, target);
-
-       read_unlock(&tasklist_lock);
-       spin_unlock(&task_capability_lock);
-
-       if (!found)
-               ret = 0;
-
-       return ret;
-}
-
-/*
- * Given the target pid does not refer to the current process we
- * need more elaborate support... (This support is not present when
- * filesystem capabilities are configured.)
- */
-static inline int do_sys_capset_other_tasks(pid_t pid, kernel_cap_t *effective,
-                                           kernel_cap_t *inheritable,
-                                           kernel_cap_t *permitted)
-{
-       struct task_struct *target;
-       int ret;
-
-       if (!capable(CAP_SETPCAP))
-               return -EPERM;
-
-       if (pid == -1)            /* all procs other than current and init */
-               return cap_set_all(effective, inheritable, permitted);
-
-       else if (pid < 0)                    /* all procs in process group */
-               return cap_set_pg(-pid, effective, inheritable, permitted);
-
-       /* target != current */
-       spin_lock(&task_capability_lock);
-       read_lock(&tasklist_lock);
-
-       target = find_task_by_vpid(pid);
-       if (!target)
-               ret = -ESRCH;
-       else {
-               ret = security_capset_check(target, effective, inheritable,
-                                           permitted);
-
-               /* having verified that the proposed changes are legal,
-                  we now put them into effect. */
-               if (!ret)
-                       security_capset_set(target, effective, inheritable,
-                                           permitted);
-       }
-
-       read_unlock(&tasklist_lock);
-       spin_unlock(&task_capability_lock);
-
-       return ret;
-}
-
-#else /* ie., def CONFIG_SECURITY_FILE_CAPABILITIES */
-
 /*
- * If we have configured with filesystem capability support, then the
- * only thing that can change the capabilities of the current process
- * is the current process. As such, we can't be in this code at the
- * same time as we are in the process of setting capabilities in this
- * process. The net result is that we can limit our use of locks to
- * when we are reading the caps of another process.
+ * The only thing that can change the capabilities of the current
+ * process is the current process. As such, we can't be in this code
+ * at the same time as we are in the process of setting capabilities
+ * in this process. The net result is that we can limit our use of
+ * locks to when we are reading the caps of another process.
  */
 static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
                                     kernel_cap_t *pIp, kernel_cap_t *pPp)
@@ -285,7 +137,6 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
        if (pid && (pid != task_pid_vnr(current))) {
                struct task_struct *target;
 
-               spin_lock(&task_capability_lock);
                read_lock(&tasklist_lock);
 
                target = find_task_by_vpid(pid);
@@ -295,50 +146,12 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
                        ret = security_capget(target, pEp, pIp, pPp);
 
                read_unlock(&tasklist_lock);
-               spin_unlock(&task_capability_lock);
        } else
                ret = security_capget(current, pEp, pIp, pPp);
 
        return ret;
 }
 
-/*
- * With filesystem capability support configured, the kernel does not
- * permit the changing of capabilities in one process by another
- * process. (CAP_SETPCAP has much less broad semantics when configured
- * this way.)
- */
-static inline int do_sys_capset_other_tasks(pid_t pid,
-                                           kernel_cap_t *effective,
-                                           kernel_cap_t *inheritable,
-                                           kernel_cap_t *permitted)
-{
-       return -EPERM;
-}
-
-#endif /* ie., ndef CONFIG_SECURITY_FILE_CAPABILITIES */
-
-/*
- * Atomically modify the effective capabilities returning the original
- * value. No permission check is performed here - it is assumed that the
- * caller is permitted to set the desired effective capabilities.
- */
-kernel_cap_t cap_set_effective(const kernel_cap_t pE_new)
-{
-       kernel_cap_t pE_old;
-
-       spin_lock(&task_capability_lock);
-
-       pE_old = current->cap_effective;
-       current->cap_effective = pE_new;
-
-       spin_unlock(&task_capability_lock);
-
-       return pE_old;
-}
-
-EXPORT_SYMBOL(cap_set_effective);
-
 /**
  * sys_capget - get the capabilities of a given process.
  * @header: pointer to struct that contains capability version and
@@ -366,7 +179,6 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr)
                return -EINVAL;
 
        ret = cap_get_target_pid(pid, &pE, &pI, &pP);
-
        if (!ret) {
                struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S];
                unsigned i;
@@ -412,16 +224,14 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr)
  * @data: pointer to struct that contains the effective, permitted,
  *     and inheritable capabilities
  *
- * Set capabilities for a given process, all processes, or all
- * processes in a given process group.
+ * Set capabilities for the current process only.  The ability to any other
+ * process(es) has been deprecated and removed.
  *
  * The restrictions on setting capabilities are specified as:
  *
- * [pid is for the 'target' task.  'current' is the calling task.]
- *
- * I: any raised capabilities must be a subset of the (old current) permitted
- * P: any raised capabilities must be a subset of the (old current) permitted
- * E: must be set to a subset of (new target) permitted
+ * I: any raised capabilities must be a subset of the old permitted
+ * P: any raised capabilities must be a subset of the old permitted
+ * E: must be set to a subset of new permitted
  *
  * Returns 0 on success and < 0 on error.
  */
@@ -430,6 +240,7 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
        struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S];
        unsigned i, tocopy;
        kernel_cap_t inheritable, permitted, effective;
+       struct cred *new;
        int ret;
        pid_t pid;
 
@@ -440,10 +251,13 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
        if (get_user(pid, &header->pid))
                return -EFAULT;
 
-       if (copy_from_user(&kdata, data, tocopy
-                          * sizeof(struct __user_cap_data_struct))) {
+       /* may only affect current now */
+       if (pid != 0 && pid != task_pid_vnr(current))
+               return -EPERM;
+
+       if (copy_from_user(&kdata, data,
+                          tocopy * sizeof(struct __user_cap_data_struct)))
                return -EFAULT;
-       }
 
        for (i = 0; i < tocopy; i++) {
                effective.cap[i] = kdata[i].effective;
@@ -457,32 +271,23 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
                i++;
        }
 
-       if (pid && (pid != task_pid_vnr(current)))
-               ret = do_sys_capset_other_tasks(pid, &effective, &inheritable,
-                                               &permitted);
-       else {
-               /*
-                * This lock is required even when filesystem
-                * capability support is configured - it protects the
-                * sys_capget() call from returning incorrect data in
-                * the case that the targeted process is not the
-                * current one.
-                */
-               spin_lock(&task_capability_lock);
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
 
-               ret = security_capset_check(current, &effective, &inheritable,
-                                           &permitted);
-               /*
-                * Having verified that the proposed changes are
-                * legal, we now put them into effect.
-                */
-               if (!ret)
-                       security_capset_set(current, &effective, &inheritable,
-                                           &permitted);
-               spin_unlock(&task_capability_lock);
-       }
+       ret = security_capset(new, current_cred(),
+                             &effective, &inheritable, &permitted);
+       if (ret < 0)
+               goto error;
+
+       ret = audit_log_capset(pid, new, current_cred());
+       if (ret < 0)
+               return ret;
 
+       return commit_creds(new);
 
+error:
+       abort_creds(new);
        return ret;
 }
 
@@ -498,6 +303,11 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
  */
 int capable(int cap)
 {
+       if (unlikely(!cap_valid(cap))) {
+               printk(KERN_CRIT "capable() called with invalid cap=%u\n", cap);
+               BUG();
+       }
+
        if (has_capability(current, cap)) {
                current->flags |= PF_SUPERPRIV;
                return 1;
index 2606d0f..48348dd 100644 (file)
@@ -571,8 +571,8 @@ static struct inode *cgroup_new_inode(mode_t mode, struct super_block *sb)
 
        if (inode) {
                inode->i_mode = mode;
-               inode->i_uid = current->fsuid;
-               inode->i_gid = current->fsgid;
+               inode->i_uid = current_fsuid();
+               inode->i_gid = current_fsgid();
                inode->i_blocks = 0;
                inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
                inode->i_mapping->backing_dev_info = &cgroup_backing_dev_info;
@@ -1280,6 +1280,7 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
 static int attach_task_by_pid(struct cgroup *cgrp, u64 pid)
 {
        struct task_struct *tsk;
+       const struct cred *cred = current_cred(), *tcred;
        int ret;
 
        if (pid) {
@@ -1289,14 +1290,16 @@ static int attach_task_by_pid(struct cgroup *cgrp, u64 pid)
                        rcu_read_unlock();
                        return -ESRCH;
                }
-               get_task_struct(tsk);
-               rcu_read_unlock();
 
-               if ((current->euid) && (current->euid != tsk->uid)
-                   && (current->euid != tsk->suid)) {
-                       put_task_struct(tsk);
+               tcred = __task_cred(tsk);
+               if (cred->euid &&
+                   cred->euid != tcred->uid &&
+                   cred->euid != tcred->suid) {
+                       rcu_read_unlock();
                        return -EACCES;
                }
+               get_task_struct(tsk);
+               rcu_read_unlock();
        } else {
                tsk = current;
                get_task_struct(tsk);
diff --git a/kernel/cred-internals.h b/kernel/cred-internals.h
new file mode 100644 (file)
index 0000000..2dc4fc2
--- /dev/null
@@ -0,0 +1,21 @@
+/* Internal credentials stuff
+ *
+ * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+/*
+ * user.c
+ */
+static inline void sched_switch_user(struct task_struct *p)
+{
+#ifdef CONFIG_USER_SCHED
+       sched_move_task(p);
+#endif /* CONFIG_USER_SCHED */
+}
+
diff --git a/kernel/cred.c b/kernel/cred.c
new file mode 100644 (file)
index 0000000..ff7bc07
--- /dev/null
@@ -0,0 +1,588 @@
+/* Task credentials management - see Documentation/credentials.txt
+ *
+ * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/cred.h>
+#include <linux/sched.h>
+#include <linux/key.h>
+#include <linux/keyctl.h>
+#include <linux/init_task.h>
+#include <linux/security.h>
+#include <linux/cn_proc.h>
+#include "cred-internals.h"
+
+static struct kmem_cache *cred_jar;
+
+/*
+ * The common credentials for the initial task's thread group
+ */
+#ifdef CONFIG_KEYS
+static struct thread_group_cred init_tgcred = {
+       .usage  = ATOMIC_INIT(2),
+       .tgid   = 0,
+       .lock   = SPIN_LOCK_UNLOCKED,
+};
+#endif
+
+/*
+ * The initial credentials for the initial task
+ */
+struct cred init_cred = {
+       .usage                  = ATOMIC_INIT(4),
+       .securebits             = SECUREBITS_DEFAULT,
+       .cap_inheritable        = CAP_INIT_INH_SET,
+       .cap_permitted          = CAP_FULL_SET,
+       .cap_effective          = CAP_INIT_EFF_SET,
+       .cap_bset               = CAP_INIT_BSET,
+       .user                   = INIT_USER,
+       .group_info             = &init_groups,
+#ifdef CONFIG_KEYS
+       .tgcred                 = &init_tgcred,
+#endif
+};
+
+/*
+ * Dispose of the shared task group credentials
+ */
+#ifdef CONFIG_KEYS
+static void release_tgcred_rcu(struct rcu_head *rcu)
+{
+       struct thread_group_cred *tgcred =
+               container_of(rcu, struct thread_group_cred, rcu);
+
+       BUG_ON(atomic_read(&tgcred->usage) != 0);
+
+       key_put(tgcred->session_keyring);
+       key_put(tgcred->process_keyring);
+       kfree(tgcred);
+}
+#endif
+
+/*
+ * Release a set of thread group credentials.
+ */
+static void release_tgcred(struct cred *cred)
+{
+#ifdef CONFIG_KEYS
+       struct thread_group_cred *tgcred = cred->tgcred;
+
+       if (atomic_dec_and_test(&tgcred->usage))
+               call_rcu(&tgcred->rcu, release_tgcred_rcu);
+#endif
+}
+
+/*
+ * The RCU callback to actually dispose of a set of credentials
+ */
+static void put_cred_rcu(struct rcu_head *rcu)
+{
+       struct cred *cred = container_of(rcu, struct cred, rcu);
+
+       if (atomic_read(&cred->usage) != 0)
+               panic("CRED: put_cred_rcu() sees %p with usage %d\n",
+                     cred, atomic_read(&cred->usage));
+
+       security_cred_free(cred);
+       key_put(cred->thread_keyring);
+       key_put(cred->request_key_auth);
+       release_tgcred(cred);
+       put_group_info(cred->group_info);
+       free_uid(cred->user);
+       kmem_cache_free(cred_jar, cred);
+}
+
+/**
+ * __put_cred - Destroy a set of credentials
+ * @cred: The record to release
+ *
+ * Destroy a set of credentials on which no references remain.
+ */
+void __put_cred(struct cred *cred)
+{
+       BUG_ON(atomic_read(&cred->usage) != 0);
+
+       call_rcu(&cred->rcu, put_cred_rcu);
+}
+EXPORT_SYMBOL(__put_cred);
+
+/**
+ * prepare_creds - Prepare a new set of credentials for modification
+ *
+ * Prepare a new set of task credentials for modification.  A task's creds
+ * shouldn't generally be modified directly, therefore this function is used to
+ * prepare a new copy, which the caller then modifies and then commits by
+ * calling commit_creds().
+ *
+ * Preparation involves making a copy of the objective creds for modification.
+ *
+ * Returns a pointer to the new creds-to-be if successful, NULL otherwise.
+ *
+ * Call commit_creds() or abort_creds() to clean up.
+ */
+struct cred *prepare_creds(void)
+{
+       struct task_struct *task = current;
+       const struct cred *old;
+       struct cred *new;
+
+       BUG_ON(atomic_read(&task->real_cred->usage) < 1);
+
+       new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
+       if (!new)
+               return NULL;
+
+       old = task->cred;
+       memcpy(new, old, sizeof(struct cred));
+
+       atomic_set(&new->usage, 1);
+       get_group_info(new->group_info);
+       get_uid(new->user);
+
+#ifdef CONFIG_KEYS
+       key_get(new->thread_keyring);
+       key_get(new->request_key_auth);
+       atomic_inc(&new->tgcred->usage);
+#endif
+
+#ifdef CONFIG_SECURITY
+       new->security = NULL;
+#endif
+
+       if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
+               goto error;
+       return new;
+
+error:
+       abort_creds(new);
+       return NULL;
+}
+EXPORT_SYMBOL(prepare_creds);
+
+/*
+ * Prepare credentials for current to perform an execve()
+ * - The caller must hold current->cred_exec_mutex
+ */
+struct cred *prepare_exec_creds(void)
+{
+       struct thread_group_cred *tgcred = NULL;
+       struct cred *new;
+
+#ifdef CONFIG_KEYS
+       tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
+       if (!tgcred)
+               return NULL;
+#endif
+
+       new = prepare_creds();
+       if (!new) {
+               kfree(tgcred);
+               return new;
+       }
+
+#ifdef CONFIG_KEYS
+       /* newly exec'd tasks don't get a thread keyring */
+       key_put(new->thread_keyring);
+       new->thread_keyring = NULL;
+
+       /* create a new per-thread-group creds for all this set of threads to
+        * share */
+       memcpy(tgcred, new->tgcred, sizeof(struct thread_group_cred));
+
+       atomic_set(&tgcred->usage, 1);
+       spin_lock_init(&tgcred->lock);
+
+       /* inherit the session keyring; new process keyring */
+       key_get(tgcred->session_keyring);
+       tgcred->process_keyring = NULL;
+
+       release_tgcred(new);
+       new->tgcred = tgcred;
+#endif
+
+       return new;
+}
+
+/*
+ * prepare new credentials for the usermode helper dispatcher
+ */
+struct cred *prepare_usermodehelper_creds(void)
+{
+#ifdef CONFIG_KEYS
+       struct thread_group_cred *tgcred = NULL;
+#endif
+       struct cred *new;
+
+#ifdef CONFIG_KEYS
+       tgcred = kzalloc(sizeof(*new->tgcred), GFP_ATOMIC);
+       if (!tgcred)
+               return NULL;
+#endif
+
+       new = kmem_cache_alloc(cred_jar, GFP_ATOMIC);
+       if (!new)
+               return NULL;
+
+       memcpy(new, &init_cred, sizeof(struct cred));
+
+       atomic_set(&new->usage, 1);
+       get_group_info(new->group_info);
+       get_uid(new->user);
+
+#ifdef CONFIG_KEYS
+       new->thread_keyring = NULL;
+       new->request_key_auth = NULL;
+       new->jit_keyring = KEY_REQKEY_DEFL_DEFAULT;
+
+       atomic_set(&tgcred->usage, 1);
+       spin_lock_init(&tgcred->lock);
+       new->tgcred = tgcred;
+#endif
+
+#ifdef CONFIG_SECURITY
+       new->security = NULL;
+#endif
+       if (security_prepare_creds(new, &init_cred, GFP_ATOMIC) < 0)
+               goto error;
+
+       BUG_ON(atomic_read(&new->usage) != 1);
+       return new;
+
+error:
+       put_cred(new);
+       return NULL;
+}
+
+/*
+ * Copy credentials for the new process created by fork()
+ *
+ * We share if we can, but under some circumstances we have to generate a new
+ * set.
+ *
+ * The new process gets the current process's subjective credentials as its
+ * objective and subjective credentials
+ */
+int copy_creds(struct task_struct *p, unsigned long clone_flags)
+{
+#ifdef CONFIG_KEYS
+       struct thread_group_cred *tgcred;
+#endif
+       struct cred *new;
+       int ret;
+
+       mutex_init(&p->cred_exec_mutex);
+
+       if (
+#ifdef CONFIG_KEYS
+               !p->cred->thread_keyring &&
+#endif
+               clone_flags & CLONE_THREAD
+           ) {
+               p->real_cred = get_cred(p->cred);
+               get_cred(p->cred);
+               atomic_inc(&p->cred->user->processes);
+               return 0;
+       }
+
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+
+       if (clone_flags & CLONE_NEWUSER) {
+               ret = create_user_ns(new);
+               if (ret < 0)
+                       goto error_put;
+       }
+
+#ifdef CONFIG_KEYS
+       /* new threads get their own thread keyrings if their parent already
+        * had one */
+       if (new->thread_keyring) {
+               key_put(new->thread_keyring);
+               new->thread_keyring = NULL;
+               if (clone_flags & CLONE_THREAD)
+                       install_thread_keyring_to_cred(new);
+       }
+
+       /* we share the process and session keyrings between all the threads in
+        * a process - this is slightly icky as we violate COW credentials a
+        * bit */
+       if (!(clone_flags & CLONE_THREAD)) {
+               tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
+               if (!tgcred) {
+                       ret = -ENOMEM;
+                       goto error_put;
+               }
+               atomic_set(&tgcred->usage, 1);
+               spin_lock_init(&tgcred->lock);
+               tgcred->process_keyring = NULL;
+               tgcred->session_keyring = key_get(new->tgcred->session_keyring);
+
+               release_tgcred(new);
+               new->tgcred = tgcred;
+       }
+#endif
+
+       atomic_inc(&new->user->processes);
+       p->cred = p->real_cred = get_cred(new);
+       return 0;
+
+error_put:
+       put_cred(new);
+       return ret;
+}
+
+/**
+ * commit_creds - Install new credentials upon the current task
+ * @new: The credentials to be assigned
+ *
+ * Install a new set of credentials to the current task, using RCU to replace
+ * the old set.  Both the objective and the subjective credentials pointers are
+ * updated.  This function may not be called if the subjective credentials are
+ * in an overridden state.
+ *
+ * This function eats the caller's reference to the new credentials.
+ *
+ * Always returns 0 thus allowing this function to be tail-called at the end
+ * of, say, sys_setgid().
+ */
+int commit_creds(struct cred *new)
+{
+       struct task_struct *task = current;
+       const struct cred *old;
+
+       BUG_ON(task->cred != task->real_cred);
+       BUG_ON(atomic_read(&task->real_cred->usage) < 2);
+       BUG_ON(atomic_read(&new->usage) < 1);
+
+       old = task->real_cred;
+       security_commit_creds(new, old);
+
+       get_cred(new); /* we will require a ref for the subj creds too */
+
+       /* dumpability changes */
+       if (old->euid != new->euid ||
+           old->egid != new->egid ||
+           old->fsuid != new->fsuid ||
+           old->fsgid != new->fsgid ||
+           !cap_issubset(new->cap_permitted, old->cap_permitted)) {
+               set_dumpable(task->mm, suid_dumpable);
+               task->pdeath_signal = 0;
+               smp_wmb();
+       }
+
+       /* alter the thread keyring */
+       if (new->fsuid != old->fsuid)
+               key_fsuid_changed(task);
+       if (new->fsgid != old->fsgid)
+               key_fsgid_changed(task);
+
+       /* do it
+        * - What if a process setreuid()'s and this brings the
+        *   new uid over his NPROC rlimit?  We can check this now
+        *   cheaply with the new uid cache, so if it matters
+        *   we should be checking for it.  -DaveM
+        */
+       if (new->user != old->user)
+               atomic_inc(&new->user->processes);
+       rcu_assign_pointer(task->real_cred, new);
+       rcu_assign_pointer(task->cred, new);
+       if (new->user != old->user)
+               atomic_dec(&old->user->processes);
+
+       sched_switch_user(task);
+
+       /* send notifications */
+       if (new->uid   != old->uid  ||
+           new->euid  != old->euid ||
+           new->suid  != old->suid ||
+           new->fsuid != old->fsuid)
+               proc_id_connector(task, PROC_EVENT_UID);
+
+       if (new->gid   != old->gid  ||
+           new->egid  != old->egid ||
+           new->sgid  != old->sgid ||
+           new->fsgid != old->fsgid)
+               proc_id_connector(task, PROC_EVENT_GID);
+
+       /* release the old obj and subj refs both */
+       put_cred(old);
+       put_cred(old);
+       return 0;
+}
+EXPORT_SYMBOL(commit_creds);
+
+/**
+ * abort_creds - Discard a set of credentials and unlock the current task
+ * @new: The credentials that were going to be applied
+ *
+ * Discard a set of credentials that were under construction and unlock the
+ * current task.
+ */
+void abort_creds(struct cred *new)
+{
+       BUG_ON(atomic_read(&new->usage) < 1);
+       put_cred(new);
+}
+EXPORT_SYMBOL(abort_creds);
+
+/**
+ * override_creds - Override the current process's subjective credentials
+ * @new: The credentials to be assigned
+ *
+ * Install a set of temporary override subjective credentials on the current
+ * process, returning the old set for later reversion.
+ */
+const struct cred *override_creds(const struct cred *new)
+{
+       const struct cred *old = current->cred;
+
+       rcu_assign_pointer(current->cred, get_cred(new));
+       return old;
+}
+EXPORT_SYMBOL(override_creds);
+
+/**
+ * revert_creds - Revert a temporary subjective credentials override
+ * @old: The credentials to be restored
+ *
+ * Revert a temporary set of override subjective credentials to an old set,
+ * discarding the override set.
+ */
+void revert_creds(const struct cred *old)
+{
+       const struct cred *override = current->cred;
+
+       rcu_assign_pointer(current->cred, old);
+       put_cred(override);
+}
+EXPORT_SYMBOL(revert_creds);
+
+/*
+ * initialise the credentials stuff
+ */
+void __init cred_init(void)
+{
+       /* allocate a slab in which we can store credentials */
+       cred_jar = kmem_cache_create("cred_jar", sizeof(struct cred),
+                                    0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
+}
+
+/**
+ * prepare_kernel_cred - Prepare a set of credentials for a kernel service
+ * @daemon: A userspace daemon to be used as a reference
+ *
+ * Prepare a set of credentials for a kernel service.  This can then be used to
+ * override a task's own credentials so that work can be done on behalf of that
+ * task that requires a different subjective context.
+ *
+ * @daemon is used to provide a base for the security record, but can be NULL.
+ * If @daemon is supplied, then the security data will be derived from that;
+ * otherwise they'll be set to 0 and no groups, full capabilities and no keys.
+ *
+ * The caller may change these controls afterwards if desired.
+ *
+ * Returns the new credentials or NULL if out of memory.
+ *
+ * Does not take, and does not return holding current->cred_replace_mutex.
+ */
+struct cred *prepare_kernel_cred(struct task_struct *daemon)
+{
+       const struct cred *old;
+       struct cred *new;
+
+       new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
+       if (!new)
+               return NULL;
+
+       if (daemon)
+               old = get_task_cred(daemon);
+       else
+               old = get_cred(&init_cred);
+
+       get_uid(new->user);
+       get_group_info(new->group_info);
+
+#ifdef CONFIG_KEYS
+       atomic_inc(&init_tgcred.usage);
+       new->tgcred = &init_tgcred;
+       new->request_key_auth = NULL;
+       new->thread_keyring = NULL;
+       new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
+#endif
+
+#ifdef CONFIG_SECURITY
+       new->security = NULL;
+#endif
+       if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
+               goto error;
+
+       atomic_set(&new->usage, 1);
+       put_cred(old);
+       return new;
+
+error:
+       put_cred(new);
+       return NULL;
+}
+EXPORT_SYMBOL(prepare_kernel_cred);
+
+/**
+ * set_security_override - Set the security ID in a set of credentials
+ * @new: The credentials to alter
+ * @secid: The LSM security ID to set
+ *
+ * Set the LSM security ID in a set of credentials so that the subjective
+ * security is overridden when an alternative set of credentials is used.
+ */
+int set_security_override(struct cred *new, u32 secid)
+{
+       return security_kernel_act_as(new, secid);
+}
+EXPORT_SYMBOL(set_security_override);
+
+/**
+ * set_security_override_from_ctx - Set the security ID in a set of credentials
+ * @new: The credentials to alter
+ * @secctx: The LSM security context to generate the security ID from.
+ *
+ * Set the LSM security ID in a set of credentials so that the subjective
+ * security is overridden when an alternative set of credentials is used.  The
+ * security ID is specified in string form as a security context to be
+ * interpreted by the LSM.
+ */
+int set_security_override_from_ctx(struct cred *new, const char *secctx)
+{
+       u32 secid;
+       int ret;
+
+       ret = security_secctx_to_secid(secctx, strlen(secctx), &secid);
+       if (ret < 0)
+               return ret;
+
+       return set_security_override(new, secid);
+}
+EXPORT_SYMBOL(set_security_override_from_ctx);
+
+/**
+ * set_create_files_as - Set the LSM file create context in a set of credentials
+ * @new: The credentials to alter
+ * @inode: The inode to take the context from
+ *
+ * Change the LSM file creation context in a set of credentials to be the same
+ * as the object context of the specified inode, so that the new inodes have
+ * the same MAC context as that inode.
+ */
+int set_create_files_as(struct cred *new, struct inode *inode)
+{
+       new->fsuid = inode->i_uid;
+       new->fsgid = inode->i_gid;
+       return security_kernel_create_files_as(new, inode);
+}
+EXPORT_SYMBOL(set_create_files_as);
index 2d8be7e..ccb8716 100644 (file)
 #include <linux/blkdev.h>
 #include <linux/task_io_accounting_ops.h>
 #include <linux/tracehook.h>
+#include <linux/init_task.h>
 #include <trace/sched.h>
 
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
 #include <asm/pgtable.h>
 #include <asm/mmu_context.h>
+#include "cred-internals.h"
 
 static void exit_mm(struct task_struct * tsk);
 
@@ -164,7 +166,10 @@ void release_task(struct task_struct * p)
        int zap_leader;
 repeat:
        tracehook_prepare_release_task(p);
-       atomic_dec(&p->user->processes);
+       /* don't need to get the RCU readlock here - the process is dead and
+        * can't be modifying its own credentials */
+       atomic_dec(&__task_cred(p)->user->processes);
+
        proc_flush_task(p);
        write_lock_irq(&tasklist_lock);
        tracehook_finish_release_task(p);
@@ -339,12 +344,12 @@ static void reparent_to_kthreadd(void)
        /* cpus_allowed? */
        /* rt_priority? */
        /* signals? */
-       security_task_reparent_to_init(current);
        memcpy(current->signal->rlim, init_task.signal->rlim,
               sizeof(current->signal->rlim));
-       atomic_inc(&(INIT_USER->__count));
+
+       atomic_inc(&init_cred.usage);
+       commit_creds(&init_cred);
        write_unlock_irq(&tasklist_lock);
-       switch_uid(INIT_USER);
 }
 
 void __set_special_pids(struct pid *pid)
@@ -1078,7 +1083,6 @@ NORET_TYPE void do_exit(long code)
        check_stack_usage();
        exit_thread();
        cgroup_exit(tsk, 1);
-       exit_keys(tsk);
 
        if (group_dead && tsk->signal->leader)
                disassociate_ctty(1);
@@ -1263,12 +1267,12 @@ static int wait_task_zombie(struct task_struct *p, int options,
        unsigned long state;
        int retval, status, traced;
        pid_t pid = task_pid_vnr(p);
+       uid_t uid = __task_cred(p)->uid;
 
        if (!likely(options & WEXITED))
                return 0;
 
        if (unlikely(options & WNOWAIT)) {
-               uid_t uid = p->uid;
                int exit_code = p->exit_code;
                int why, status;
 
@@ -1389,7 +1393,7 @@ static int wait_task_zombie(struct task_struct *p, int options,
        if (!retval && infop)
                retval = put_user(pid, &infop->si_pid);
        if (!retval && infop)
-               retval = put_user(p->uid, &infop->si_uid);
+               retval = put_user(uid, &infop->si_uid);
        if (!retval)
                retval = pid;
 
@@ -1454,7 +1458,8 @@ static int wait_task_stopped(int ptrace, struct task_struct *p,
        if (!unlikely(options & WNOWAIT))
                p->exit_code = 0;
 
-       uid = p->uid;
+       /* don't need the RCU readlock here as we're holding a spinlock */
+       uid = __task_cred(p)->uid;
 unlock_sig:
        spin_unlock_irq(&p->sighand->siglock);
        if (!exit_code)
@@ -1528,10 +1533,10 @@ static int wait_task_continued(struct task_struct *p, int options,
        }
        if (!unlikely(options & WNOWAIT))
                p->signal->flags &= ~SIGNAL_STOP_CONTINUED;
+       uid = __task_cred(p)->uid;
        spin_unlock_irq(&p->sighand->siglock);
 
        pid = task_pid_vnr(p);
-       uid = p->uid;
        get_task_struct(p);
        read_unlock(&tasklist_lock);
 
index 495da2e..4e8ca23 100644 (file)
@@ -147,9 +147,8 @@ void __put_task_struct(struct task_struct *tsk)
        WARN_ON(atomic_read(&tsk->usage));
        WARN_ON(tsk == current);
 
-       security_task_free(tsk);
-       free_uid(tsk->user);
-       put_group_info(tsk->group_info);
+       put_cred(tsk->real_cred);
+       put_cred(tsk->cred);
        delayacct_tsk_free(tsk);
 
        if (!profile_handoff_task(tsk))
@@ -818,12 +817,6 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
        if (!sig)
                return -ENOMEM;
 
-       ret = copy_thread_group_keys(tsk);
-       if (ret < 0) {
-               kmem_cache_free(signal_cachep, sig);
-               return ret;
-       }
-
        atomic_set(&sig->count, 1);
        atomic_set(&sig->live, 1);
        init_waitqueue_head(&sig->wait_chldexit);
@@ -868,7 +861,6 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
 void __cleanup_signal(struct signal_struct *sig)
 {
        thread_group_cputime_free(sig);
-       exit_thread_group_keys(sig);
        tty_kref_put(sig->tty);
        kmem_cache_free(signal_cachep, sig);
 }
@@ -984,16 +976,16 @@ static struct task_struct *copy_process(unsigned long clone_flags,
        DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
 #endif
        retval = -EAGAIN;
-       if (atomic_read(&p->user->processes) >=
+       if (atomic_read(&p->real_cred->user->processes) >=
                        p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
                if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
-                   p->user != current->nsproxy->user_ns->root_user)
+                   p->real_cred->user != INIT_USER)
                        goto bad_fork_free;
        }
 
-       atomic_inc(&p->user->__count);
-       atomic_inc(&p->user->processes);
-       get_group_info(p->group_info);
+       retval = copy_creds(p, clone_flags);
+       if (retval < 0)
+               goto bad_fork_free;
 
        /*
         * If multiple threads are within copy_process(), then this check
@@ -1048,10 +1040,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
        do_posix_clock_monotonic_gettime(&p->start_time);
        p->real_start_time = p->start_time;
        monotonic_to_bootbased(&p->real_start_time);
-#ifdef CONFIG_SECURITY
-       p->security = NULL;
-#endif
-       p->cap_bset = current->cap_bset;
        p->io_context = NULL;
        p->audit_context = NULL;
        cgroup_fork(p);
@@ -1096,10 +1084,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
        /* Perform scheduler related setup. Assign this task to a CPU. */
        sched_fork(p, clone_flags);
 
-       if ((retval = security_task_alloc(p)))
-               goto bad_fork_cleanup_policy;
        if ((retval = audit_alloc(p)))
-               goto bad_fork_cleanup_security;
+               goto bad_fork_cleanup_policy;
        /* copy all the process information */
        if ((retval = copy_semundo(clone_flags, p)))
                goto bad_fork_cleanup_audit;
@@ -1113,10 +1099,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
                goto bad_fork_cleanup_sighand;
        if ((retval = copy_mm(clone_flags, p)))
                goto bad_fork_cleanup_signal;
-       if ((retval = copy_keys(clone_flags, p)))
-               goto bad_fork_cleanup_mm;
        if ((retval = copy_namespaces(clone_flags, p)))
-               goto bad_fork_cleanup_keys;
+               goto bad_fork_cleanup_mm;
        if ((retval = copy_io(clone_flags, p)))
                goto bad_fork_cleanup_namespaces;
        retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
@@ -1281,8 +1265,6 @@ bad_fork_cleanup_io:
        put_io_context(p->io_context);
 bad_fork_cleanup_namespaces:
        exit_task_namespaces(p);
-bad_fork_cleanup_keys:
-       exit_keys(p);
 bad_fork_cleanup_mm:
        if (p->mm)
                mmput(p->mm);
@@ -1298,8 +1280,6 @@ bad_fork_cleanup_semundo:
        exit_sem(p);
 bad_fork_cleanup_audit:
        audit_free(p);
-bad_fork_cleanup_security:
-       security_task_free(p);
 bad_fork_cleanup_policy:
 #ifdef CONFIG_NUMA
        mpol_put(p->mempolicy);
@@ -1312,9 +1292,9 @@ bad_fork_cleanup_cgroup:
 bad_fork_cleanup_put_domain:
        module_put(task_thread_info(p)->exec_domain->module);
 bad_fork_cleanup_count:
-       put_group_info(p->group_info);
-       atomic_dec(&p->user->processes);
-       free_uid(p->user);
+       atomic_dec(&p->cred->user->processes);
+       put_cred(p->real_cred);
+       put_cred(p->cred);
 bad_fork_free:
        free_task(p);
 fork_out:
@@ -1357,6 +1337,21 @@ long do_fork(unsigned long clone_flags,
        int trace = 0;
        long nr;
 
+       /*
+        * Do some preliminary argument and permissions checking before we
+        * actually start allocating stuff
+        */
+       if (clone_flags & CLONE_NEWUSER) {
+               if (clone_flags & CLONE_THREAD)
+                       return -EINVAL;
+               /* hopefully this check will go away when userns support is
+                * complete
+                */
+               if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SETUID) ||
+                               !capable(CAP_SETGID))
+                       return -EPERM;
+       }
+
        /*
         * We hope to recycle these flags after 2.6.26
         */
@@ -1605,8 +1600,7 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
        err = -EINVAL;
        if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND|
                                CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
-                               CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWUSER|
-                               CLONE_NEWNET))
+                               CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET))
                goto bad_unshare_out;
 
        /*
index 8af1002..4fe790e 100644 (file)
@@ -439,13 +439,20 @@ static void free_pi_state(struct futex_pi_state *pi_state)
 static struct task_struct * futex_find_get_task(pid_t pid)
 {
        struct task_struct *p;
+       const struct cred *cred = current_cred(), *pcred;
 
        rcu_read_lock();
        p = find_task_by_vpid(pid);
-       if (!p || ((current->euid != p->euid) && (current->euid != p->uid)))
+       if (!p) {
                p = ERR_PTR(-ESRCH);
-       else
-               get_task_struct(p);
+       } else {
+               pcred = __task_cred(p);
+               if (cred->euid != pcred->euid &&
+                   cred->euid != pcred->uid)
+                       p = ERR_PTR(-ESRCH);
+               else
+                       get_task_struct(p);
+       }
 
        rcu_read_unlock();
 
@@ -1829,6 +1836,7 @@ sys_get_robust_list(int pid, struct robust_list_head __user * __user *head_ptr,
 {
        struct robust_list_head __user *head;
        unsigned long ret;
+       const struct cred *cred = current_cred(), *pcred;
 
        if (!futex_cmpxchg_enabled)
                return -ENOSYS;
@@ -1844,8 +1852,10 @@ sys_get_robust_list(int pid, struct robust_list_head __user * __user *head_ptr,
                if (!p)
                        goto err_unlock;
                ret = -EPERM;
-               if ((current->euid != p->euid) && (current->euid != p->uid) &&
-                               !capable(CAP_SYS_PTRACE))
+               pcred = __task_cred(p);
+               if (cred->euid != pcred->euid &&
+                   cred->euid != pcred->uid &&
+                   !capable(CAP_SYS_PTRACE))
                        goto err_unlock;
                head = p->robust_list;
                rcu_read_unlock();
index 04ac3a9..d607a5b 100644 (file)
@@ -135,6 +135,7 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr,
 {
        struct compat_robust_list_head __user *head;
        unsigned long ret;
+       const struct cred *cred = current_cred(), *pcred;
 
        if (!futex_cmpxchg_enabled)
                return -ENOSYS;
@@ -150,8 +151,10 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr,
                if (!p)
                        goto err_unlock;
                ret = -EPERM;
-               if ((current->euid != p->euid) && (current->euid != p->uid) &&
-                               !capable(CAP_SYS_PTRACE))
+               pcred = __task_cred(p);
+               if (cred->euid != pcred->euid &&
+                   cred->euid != pcred->uid &&
+                   !capable(CAP_SYS_PTRACE))
                        goto err_unlock;
                head = p->compat_robust_list;
                read_unlock(&tasklist_lock);
index 3d3c3ea..b46dbb9 100644 (file)
@@ -118,10 +118,10 @@ EXPORT_SYMBOL(request_module);
 struct subprocess_info {
        struct work_struct work;
        struct completion *complete;
+       struct cred *cred;
        char *path;
        char **argv;
        char **envp;
-       struct key *ring;
        enum umh_wait wait;
        int retval;
        struct file *stdin;
@@ -134,19 +134,20 @@ struct subprocess_info {
 static int ____call_usermodehelper(void *data)
 {
        struct subprocess_info *sub_info = data;
-       struct key *new_session, *old_session;
        int retval;
 
-       /* Unblock all signals and set the session keyring. */
-       new_session = key_get(sub_info->ring);
+       BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
+
+       /* Unblock all signals */
        spin_lock_irq(&current->sighand->siglock);
-       old_session = __install_session_keyring(current, new_session);
        flush_signal_handlers(current, 1);
        sigemptyset(&current->blocked);
        recalc_sigpending();
        spin_unlock_irq(&current->sighand->siglock);
 
-       key_put(old_session);
+       /* Install the credentials */
+       commit_creds(sub_info->cred);
+       sub_info->cred = NULL;
 
        /* Install input pipe when needed */
        if (sub_info->stdin) {
@@ -185,6 +186,8 @@ void call_usermodehelper_freeinfo(struct subprocess_info *info)
 {
        if (info->cleanup)
                (*info->cleanup)(info->argv, info->envp);
+       if (info->cred)
+               put_cred(info->cred);
        kfree(info);
 }
 EXPORT_SYMBOL(call_usermodehelper_freeinfo);
@@ -240,6 +243,8 @@ static void __call_usermodehelper(struct work_struct *work)
        pid_t pid;
        enum umh_wait wait = sub_info->wait;
 
+       BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
+
        /* CLONE_VFORK: wait until the usermode helper has execve'd
         * successfully We need the data structures to stay around
         * until that is done.  */
@@ -362,6 +367,9 @@ struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
        sub_info->path = path;
        sub_info->argv = argv;
        sub_info->envp = envp;
+       sub_info->cred = prepare_usermodehelper_creds();
+       if (!sub_info->cred)
+               return NULL;
 
   out:
        return sub_info;
@@ -376,7 +384,13 @@ EXPORT_SYMBOL(call_usermodehelper_setup);
 void call_usermodehelper_setkeys(struct subprocess_info *info,
                                 struct key *session_keyring)
 {
-       info->ring = session_keyring;
+#ifdef CONFIG_KEYS
+       struct thread_group_cred *tgcred = info->cred->tgcred;
+       key_put(tgcred->session_keyring);
+       tgcred->session_keyring = key_get(session_keyring);
+#else
+       BUG();
+#endif
 }
 EXPORT_SYMBOL(call_usermodehelper_setkeys);
 
@@ -444,6 +458,8 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,
        DECLARE_COMPLETION_ONSTACK(done);
        int retval = 0;
 
+       BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
+
        helper_lock();
        if (sub_info->path[0] == '\0')
                goto out;
index 1d3ef29..63598dc 100644 (file)
@@ -80,12 +80,6 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
                goto out_pid;
        }
 
-       new_nsp->user_ns = copy_user_ns(flags, tsk->nsproxy->user_ns);
-       if (IS_ERR(new_nsp->user_ns)) {
-               err = PTR_ERR(new_nsp->user_ns);
-               goto out_user;
-       }
-
        new_nsp->net_ns = copy_net_ns(flags, tsk->nsproxy->net_ns);
        if (IS_ERR(new_nsp->net_ns)) {
                err = PTR_ERR(new_nsp->net_ns);
@@ -95,9 +89,6 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
        return new_nsp;
 
 out_net:
-       if (new_nsp->user_ns)
-               put_user_ns(new_nsp->user_ns);
-out_user:
        if (new_nsp->pid_ns)
                put_pid_ns(new_nsp->pid_ns);
 out_pid:
@@ -130,7 +121,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
        get_nsproxy(old_ns);
 
        if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
-                               CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNET)))
+                               CLONE_NEWPID | CLONE_NEWNET)))
                return 0;
 
        if (!capable(CAP_SYS_ADMIN)) {
@@ -173,8 +164,6 @@ void free_nsproxy(struct nsproxy *ns)
                put_ipc_ns(ns->ipc_ns);
        if (ns->pid_ns)
                put_pid_ns(ns->pid_ns);
-       if (ns->user_ns)
-               put_user_ns(ns->user_ns);
        put_net(ns->net_ns);
        kmem_cache_free(nsproxy_cachep, ns);
 }
@@ -189,7 +178,7 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags,
        int err = 0;
 
        if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
-                              CLONE_NEWUSER | CLONE_NEWNET)))
+                              CLONE_NEWNET)))
                return 0;
 
        if (!capable(CAP_SYS_ADMIN))
index 4c8bcd7..ca2df68 100644 (file)
@@ -115,6 +115,8 @@ int ptrace_check_attach(struct task_struct *child, int kill)
 
 int __ptrace_may_access(struct task_struct *task, unsigned int mode)
 {
+       const struct cred *cred = current_cred(), *tcred;
+
        /* May we inspect the given task?
         * This check is used both for attaching with ptrace
         * and for allowing access to sensitive information in /proc.
@@ -127,13 +129,19 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)
        /* Don't let security modules deny introspection */
        if (task == current)
                return 0;
-       if (((current->uid != task->euid) ||
-            (current->uid != task->suid) ||
-            (current->uid != task->uid) ||
-            (current->gid != task->egid) ||
-            (current->gid != task->sgid) ||
-            (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
+       rcu_read_lock();
+       tcred = __task_cred(task);
+       if ((cred->uid != tcred->euid ||
+            cred->uid != tcred->suid ||
+            cred->uid != tcred->uid  ||
+            cred->gid != tcred->egid ||
+            cred->gid != tcred->sgid ||
+            cred->gid != tcred->gid) &&
+           !capable(CAP_SYS_PTRACE)) {
+               rcu_read_unlock();
                return -EPERM;
+       }
+       rcu_read_unlock();
        smp_rmb();
        if (task->mm)
                dumpable = get_dumpable(task->mm);
@@ -163,6 +171,14 @@ int ptrace_attach(struct task_struct *task)
        if (same_thread_group(task, current))
                goto out;
 
+       /* Protect exec's credential calculations against our interference;
+        * SUID, SGID and LSM creds get determined differently under ptrace.
+        */
+       retval = mutex_lock_interruptible(&current->cred_exec_mutex);
+       if (retval  < 0)
+               goto out;
+
+       retval = -EPERM;
 repeat:
        /*
         * Nasty, nasty.
@@ -202,6 +218,7 @@ repeat:
 bad:
        write_unlock_irqrestore(&tasklist_lock, flags);
        task_unlock(task);
+       mutex_unlock(&current->cred_exec_mutex);
 out:
        return retval;
 }
index e4bb1dd..33cf4a1 100644 (file)
@@ -345,7 +345,9 @@ static inline struct task_group *task_group(struct task_struct *p)
        struct task_group *tg;
 
 #ifdef CONFIG_USER_SCHED
-       tg = p->user->tg;
+       rcu_read_lock();
+       tg = __task_cred(p)->user->tg;
+       rcu_read_unlock();
 #elif defined(CONFIG_CGROUP_SCHED)
        tg = container_of(task_subsys_state(p, cpu_cgroup_subsys_id),
                                struct task_group, css);
@@ -5134,6 +5136,22 @@ __setscheduler(struct rq *rq, struct task_struct *p, int policy, int prio)
        set_load_weight(p);
 }
 
+/*
+ * check the target process has a UID that matches the current process's
+ */
+static bool check_same_owner(struct task_struct *p)
+{
+       const struct cred *cred = current_cred(), *pcred;
+       bool match;
+
+       rcu_read_lock();
+       pcred = __task_cred(p);
+       match = (cred->euid == pcred->euid ||
+                cred->euid == pcred->uid);
+       rcu_read_unlock();
+       return match;
+}
+
 static int __sched_setscheduler(struct task_struct *p, int policy,
                                struct sched_param *param, bool user)
 {
@@ -5193,8 +5211,7 @@ recheck:
                        return -EPERM;
 
                /* can't change other user's priorities */
-               if ((current->euid != p->euid) &&
-                   (current->euid != p->uid))
+               if (!check_same_owner(p))
                        return -EPERM;
        }
 
@@ -5426,8 +5443,7 @@ long sched_setaffinity(pid_t pid, const cpumask_t *in_mask)
        read_unlock(&tasklist_lock);
 
        retval = -EPERM;
-       if ((current->euid != p->euid) && (current->euid != p->uid) &&
-                       !capable(CAP_SYS_NICE))
+       if (!check_same_owner(p) && !capable(CAP_SYS_NICE))
                goto out_unlock;
 
        retval = security_task_setscheduler(p, 0, NULL);
index 4530fc6..2a64304 100644 (file)
@@ -177,6 +177,11 @@ int next_signal(struct sigpending *pending, sigset_t *mask)
        return sig;
 }
 
+/*
+ * allocate a new signal queue record
+ * - this may be called without locks if and only if t == current, otherwise an
+ *   appopriate lock must be held to stop the target task from exiting
+ */
 static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,
                                         int override_rlimit)
 {
@@ -184,11 +189,12 @@ static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,
        struct user_struct *user;
 
        /*
-        * In order to avoid problems with "switch_user()", we want to make
-        * sure that the compiler doesn't re-load "t->user"
+        * We won't get problems with the target's UID changing under us
+        * because changing it requires RCU be used, and if t != current, the
+        * caller must be holding the RCU readlock (by way of a spinlock) and
+        * we use RCU protection here
         */
-       user = t->user;
-       barrier();
+       user = get_uid(__task_cred(t)->user);
        atomic_inc(&user->sigpending);
        if (override_rlimit ||
            atomic_read(&user->sigpending) <=
@@ -196,12 +202,14 @@ static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,
                q = kmem_cache_alloc(sigqueue_cachep, flags);
        if (unlikely(q == NULL)) {
                atomic_dec(&user->sigpending);
+               free_uid(user);
        } else {
                INIT_LIST_HEAD(&q->list);
                q->flags = 0;
-               q->user = get_uid(user);
+               q->user = user;
        }
-       return(q);
+
+       return q;
 }
 
 static void __sigqueue_free(struct sigqueue *q)
@@ -562,10 +570,12 @@ static int rm_from_queue(unsigned long mask, struct sigpending *s)
 
 /*
  * Bad permissions for sending the signal
+ * - the caller must hold at least the RCU read lock
  */
 static int check_kill_permission(int sig, struct siginfo *info,
                                 struct task_struct *t)
 {
+       const struct cred *cred = current_cred(), *tcred;
        struct pid *sid;
        int error;
 
@@ -579,8 +589,11 @@ static int check_kill_permission(int sig, struct siginfo *info,
        if (error)
                return error;
 
-       if ((current->euid ^ t->suid) && (current->euid ^ t->uid) &&
-           (current->uid  ^ t->suid) && (current->uid  ^ t->uid) &&
+       tcred = __task_cred(t);
+       if ((cred->euid ^ tcred->suid) &&
+           (cred->euid ^ tcred->uid) &&
+           (cred->uid  ^ tcred->suid) &&
+           (cred->uid  ^ tcred->uid) &&
            !capable(CAP_KILL)) {
                switch (sig) {
                case SIGCONT:
@@ -844,7 +857,7 @@ static int send_signal(int sig, struct siginfo *info, struct task_struct *t,
                        q->info.si_errno = 0;
                        q->info.si_code = SI_USER;
                        q->info.si_pid = task_pid_vnr(current);
-                       q->info.si_uid = current->uid;
+                       q->info.si_uid = current_uid();
                        break;
                case (unsigned long) SEND_SIG_PRIV:
                        q->info.si_signo = sig;
@@ -1008,6 +1021,10 @@ struct sighand_struct *lock_task_sighand(struct task_struct *tsk, unsigned long
        return sighand;
 }
 
+/*
+ * send signal info to all the members of a group
+ * - the caller must hold the RCU read lock at least
+ */
 int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
 {
        unsigned long flags;
@@ -1029,8 +1046,8 @@ int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
 /*
  * __kill_pgrp_info() sends a signal to a process group: this is what the tty
  * control characters do (^C, ^Z etc)
+ * - the caller must hold at least a readlock on tasklist_lock
  */
-
 int __kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp)
 {
        struct task_struct *p = NULL;
@@ -1086,6 +1103,7 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid,
 {
        int ret = -EINVAL;
        struct task_struct *p;
+       const struct cred *pcred;
 
        if (!valid_signal(sig))
                return ret;
@@ -1096,9 +1114,11 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid,
                ret = -ESRCH;
                goto out_unlock;
        }
-       if ((info == SEND_SIG_NOINFO || (!is_si_special(info) && SI_FROMUSER(info)))
-           && (euid != p->suid) && (euid != p->uid)
-           && (uid != p->suid) && (uid != p->uid)) {
+       pcred = __task_cred(p);
+       if ((info == SEND_SIG_NOINFO ||
+            (!is_si_special(info) && SI_FROMUSER(info))) &&
+           euid != pcred->suid && euid != pcred->uid &&
+           uid  != pcred->suid && uid  != pcred->uid) {
                ret = -EPERM;
                goto out_unlock;
        }
@@ -1369,10 +1389,9 @@ int do_notify_parent(struct task_struct *tsk, int sig)
         */
        rcu_read_lock();
        info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns);
+       info.si_uid = __task_cred(tsk)->uid;
        rcu_read_unlock();
 
-       info.si_uid = tsk->uid;
-
        thread_group_cputime(tsk, &cputime);
        info.si_utime = cputime_to_jiffies(cputime.utime);
        info.si_stime = cputime_to_jiffies(cputime.stime);
@@ -1440,10 +1459,9 @@ static void do_notify_parent_cldstop(struct task_struct *tsk, int why)
         */
        rcu_read_lock();
        info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns);
+       info.si_uid = __task_cred(tsk)->uid;
        rcu_read_unlock();
 
-       info.si_uid = tsk->uid;
-
        info.si_utime = cputime_to_clock_t(tsk->utime);
        info.si_stime = cputime_to_clock_t(tsk->stime);
 
@@ -1598,7 +1616,7 @@ void ptrace_notify(int exit_code)
        info.si_signo = SIGTRAP;
        info.si_code = exit_code;
        info.si_pid = task_pid_vnr(current);
-       info.si_uid = current->uid;
+       info.si_uid = current_uid();
 
        /* Let the debugger run.  */
        spin_lock_irq(&current->sighand->siglock);
@@ -1710,7 +1728,7 @@ static int ptrace_signal(int signr, siginfo_t *info,
                info->si_errno = 0;
                info->si_code = SI_USER;
                info->si_pid = task_pid_vnr(current->parent);
-               info->si_uid = current->parent->uid;
+               info->si_uid = task_uid(current->parent);
        }
 
        /* If the (new) signal is now blocked, requeue it.  */
@@ -2211,7 +2229,7 @@ sys_kill(pid_t pid, int sig)
        info.si_errno = 0;
        info.si_code = SI_USER;
        info.si_pid = task_tgid_vnr(current);
-       info.si_uid = current->uid;
+       info.si_uid = current_uid();
 
        return kill_something_info(sig, &info, pid);
 }
@@ -2228,7 +2246,7 @@ static int do_tkill(pid_t tgid, pid_t pid, int sig)
        info.si_errno = 0;
        info.si_code = SI_TKILL;
        info.si_pid = task_tgid_vnr(current);
-       info.si_uid = current->uid;
+       info.si_uid = current_uid();
 
        rcu_read_lock();
        p = find_task_by_vpid(pid);
index 31deba8..ebe65c2 100644 (file)
@@ -112,12 +112,17 @@ EXPORT_SYMBOL(cad_pid);
 
 void (*pm_power_off_prepare)(void);
 
+/*
+ * set the priority of a task
+ * - the caller must hold the RCU read lock
+ */
 static int set_one_prio(struct task_struct *p, int niceval, int error)
 {
+       const struct cred *cred = current_cred(), *pcred = __task_cred(p);
        int no_nice;
 
-       if (p->uid != current->euid &&
-               p->euid != current->euid && !capable(CAP_SYS_NICE)) {
+       if (pcred->uid  != cred->euid &&
+           pcred->euid != cred->euid && !capable(CAP_SYS_NICE)) {
                error = -EPERM;
                goto out;
        }
@@ -141,6 +146,7 @@ asmlinkage long sys_setpriority(int which, int who, int niceval)
 {
        struct task_struct *g, *p;
        struct user_struct *user;
+       const struct cred *cred = current_cred();
        int error = -EINVAL;
        struct pid *pgrp;
 
@@ -174,18 +180,18 @@ asmlinkage long sys_setpriority(int which, int who, int niceval)
                        } while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
                        break;
                case PRIO_USER:
-                       user = current->user;
+                       user = (struct user_struct *) cred->user;
                        if (!who)
-                               who = current->uid;
-                       else
-                               if ((who != current->uid) && !(user = find_user(who)))
-                                       goto out_unlock;        /* No processes for this user */
+                               who = cred->uid;
+                       else if ((who != cred->uid) &&
+                                !(user = find_user(who)))
+                               goto out_unlock;        /* No processes for this user */
 
                        do_each_thread(g, p)
-                               if (p->uid == who)
+                               if (__task_cred(p)->uid == who)
                                        error = set_one_prio(p, niceval, error);
                        while_each_thread(g, p);
-                       if (who != current->uid)
+                       if (who != cred->uid)
                                free_uid(user);         /* For find_user() */
                        break;
        }
@@ -205,6 +211,7 @@ asmlinkage long sys_getpriority(int which, int who)
 {
        struct task_struct *g, *p;
        struct user_struct *user;
+       const struct cred *cred = current_cred();
        long niceval, retval = -ESRCH;
        struct pid *pgrp;
 
@@ -236,21 +243,21 @@ asmlinkage long sys_getpriority(int which, int who)
                        } while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
                        break;
                case PRIO_USER:
-                       user = current->user;
+                       user = (struct user_struct *) cred->user;
                        if (!who)
-                               who = current->uid;
-                       else
-                               if ((who != current->uid) && !(user = find_user(who)))
-                                       goto out_unlock;        /* No processes for this user */
+                               who = cred->uid;
+                       else if ((who != cred->uid) &&
+                                !(user = find_user(who)))
+                               goto out_unlock;        /* No processes for this user */
 
                        do_each_thread(g, p)
-                               if (p->uid == who) {
+                               if (__task_cred(p)->uid == who) {
                                        niceval = 20 - task_nice(p);
                                        if (niceval > retval)
                                                retval = niceval;
                                }
                        while_each_thread(g, p);
-                       if (who != current->uid)
+                       if (who != cred->uid)
                                free_uid(user);         /* for find_user() */
                        break;
        }
@@ -472,46 +479,48 @@ void ctrl_alt_del(void)
  */
 asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
 {
-       int old_rgid = current->gid;
-       int old_egid = current->egid;
-       int new_rgid = old_rgid;
-       int new_egid = old_egid;
+       const struct cred *old;
+       struct cred *new;
        int retval;
 
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+       old = current_cred();
+
        retval = security_task_setgid(rgid, egid, (gid_t)-1, LSM_SETID_RE);
        if (retval)
-               return retval;
+               goto error;
 
+       retval = -EPERM;
        if (rgid != (gid_t) -1) {
-               if ((old_rgid == rgid) ||
-                   (current->egid==rgid) ||
+               if (old->gid == rgid ||
+                   old->egid == rgid ||
                    capable(CAP_SETGID))
-                       new_rgid = rgid;
+                       new->gid = rgid;
                else
-                       return -EPERM;
+                       goto error;
        }
        if (egid != (gid_t) -1) {
-               if ((old_rgid == egid) ||
-                   (current->egid == egid) ||
-                   (current->sgid == egid) ||
+               if (old->gid == egid ||
+                   old->egid == egid ||
+                   old->sgid == egid ||
                    capable(CAP_SETGID))
-                       new_egid = egid;
+                       new->egid = egid;
                else
-                       return -EPERM;
-       }
-       if (new_egid != old_egid) {
-               set_dumpable(current->mm, suid_dumpable);
-               smp_wmb();
+                       goto error;
        }
+
        if (rgid != (gid_t) -1 ||
-           (egid != (gid_t) -1 && egid != old_rgid))
-               current->sgid = new_egid;
-       current->fsgid = new_egid;
-       current->egid = new_egid;
-       current->gid = new_rgid;
-       key_fsgid_changed(current);
-       proc_id_connector(current, PROC_EVENT_GID);
-       return 0;
+           (egid != (gid_t) -1 && egid != old->gid))
+               new->sgid = new->egid;
+       new->fsgid = new->egid;
+
+       return commit_creds(new);
+
+error:
+       abort_creds(new);
+       return retval;
 }
 
 /*
@@ -521,56 +530,54 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
  */
 asmlinkage long sys_setgid(gid_t gid)
 {
-       int old_egid = current->egid;
+       const struct cred *old;
+       struct cred *new;
        int retval;
 
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+       old = current_cred();
+
        retval = security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_ID);
        if (retval)
-               return retval;
+               goto error;
 
-       if (capable(CAP_SETGID)) {
-               if (old_egid != gid) {
-                       set_dumpable(current->mm, suid_dumpable);
-                       smp_wmb();
-               }
-               current->gid = current->egid = current->sgid = current->fsgid = gid;
-       } else if ((gid == current->gid) || (gid == current->sgid)) {
-               if (old_egid != gid) {
-                       set_dumpable(current->mm, suid_dumpable);
-                       smp_wmb();
-               }
-               current->egid = current->fsgid = gid;
-       }
+       retval = -EPERM;
+       if (capable(CAP_SETGID))
+               new->gid = new->egid = new->sgid = new->fsgid = gid;
+       else if (gid == old->gid || gid == old->sgid)
+               new->egid = new->fsgid = gid;
        else
-               return -EPERM;
+               goto error;
 
-       key_fsgid_changed(current);
-       proc_id_connector(current, PROC_EVENT_GID);
-       return 0;
+       return commit_creds(new);
+
+error:
+       abort_creds(new);
+       return retval;
 }
   
-static int set_user(uid_t new_ruid, int dumpclear)
+/*
+ * change the user struct in a credentials set to match the new UID
+ */
+static int set_user(struct cred *new)
 {
        struct user_struct *new_user;
 
-       new_user = alloc_uid(current->nsproxy->user_ns, new_ruid);
+       new_user = alloc_uid(current_user_ns(), new->uid);
        if (!new_user)
                return -EAGAIN;
 
        if (atomic_read(&new_user->processes) >=
                                current->signal->rlim[RLIMIT_NPROC].rlim_cur &&
-                       new_user != current->nsproxy->user_ns->root_user) {
+                       new_user != INIT_USER) {
                free_uid(new_user);
                return -EAGAIN;
        }
 
-       switch_uid(new_user);
-
-       if (dumpclear) {
-               set_dumpable(current->mm, suid_dumpable);
-               smp_wmb();
-       }
-       current->uid = new_ruid;
+       free_uid(new->user);
+       new->user = new_user;
        return 0;
 }
 
@@ -591,54 +598,56 @@ static int set_user(uid_t new_ruid, int dumpclear)
  */
 asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
 {
-       int old_ruid, old_euid, old_suid, new_ruid, new_euid;
+       const struct cred *old;
+       struct cred *new;
        int retval;
 
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+       old = current_cred();
+
        retval = security_task_setuid(ruid, euid, (uid_t)-1, LSM_SETID_RE);
        if (retval)
-               return retval;
-
-       new_ruid = old_ruid = current->uid;
-       new_euid = old_euid = current->euid;
-       old_suid = current->suid;
+               goto error;
 
+       retval = -EPERM;
        if (ruid != (uid_t) -1) {
-               new_ruid = ruid;
-               if ((old_ruid != ruid) &&
-                   (current->euid != ruid) &&
+               new->uid = ruid;
+               if (old->uid != ruid &&
+                   old->euid != ruid &&
                    !capable(CAP_SETUID))
-                       return -EPERM;
+                       goto error;
        }
 
        if (euid != (uid_t) -1) {
-               new_euid = euid;
-               if ((old_ruid != euid) &&
-                   (current->euid != euid) &&
-                   (current->suid != euid) &&
+               new->euid = euid;
+               if (old->uid != euid &&
+                   old->euid != euid &&
+                   old->suid != euid &&
                    !capable(CAP_SETUID))
-                       return -EPERM;
+                       goto error;
        }
 
-       if (new_ruid != old_ruid && set_user(new_ruid, new_euid != old_euid) < 0)
-               return -EAGAIN;
+       retval = -EAGAIN;
+       if (new->uid != old->uid && set_user(new) < 0)
+               goto error;
 
-       if (new_euid != old_euid) {
-               set_dumpable(current->mm, suid_dumpable);
-               smp_wmb();
-       }
-       current->fsuid = current->euid = new_euid;
        if (ruid != (uid_t) -1 ||
-           (euid != (uid_t) -1 && euid != old_ruid))
-               current->suid = current->euid;
-       current->fsuid = current->euid;
+           (euid != (uid_t) -1 && euid != old->uid))
+               new->suid = new->euid;
+       new->fsuid = new->euid;
 
-       key_fsuid_changed(current);
-       proc_id_connector(current, PROC_EVENT_UID);
-
-       return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RE);
-}
+       retval = security_task_fix_setuid(new, old, LSM_SETID_RE);
+       if (retval < 0)
+               goto error;
 
+       return commit_creds(new);
 
+error:
+       abort_creds(new);
+       return retval;
+}
                
 /*
  * setuid() is implemented like SysV with SAVED_IDS 
@@ -653,36 +662,41 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
  */
 asmlinkage long sys_setuid(uid_t uid)
 {
-       int old_euid = current->euid;
-       int old_ruid, old_suid, new_suid;
+       const struct cred *old;
+       struct cred *new;
        int retval;
 
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+       old = current_cred();
+
        retval = security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID);
        if (retval)
-               return retval;
+               goto error;
 
-       old_ruid = current->uid;
-       old_suid = current->suid;
-       new_suid = old_suid;
-       
+       retval = -EPERM;
        if (capable(CAP_SETUID)) {
-               if (uid != old_ruid && set_user(uid, old_euid != uid) < 0)
-                       return -EAGAIN;
-               new_suid = uid;
-       } else if ((uid != current->uid) && (uid != new_suid))
-               return -EPERM;
-
-       if (old_euid != uid) {
-               set_dumpable(current->mm, suid_dumpable);
-               smp_wmb();
+               new->suid = new->uid = uid;
+               if (uid != old->uid && set_user(new) < 0) {
+                       retval = -EAGAIN;
+                       goto error;
+               }
+       } else if (uid != old->uid && uid != new->suid) {
+               goto error;
        }
-       current->fsuid = current->euid = uid;
-       current->suid = new_suid;
 
-       key_fsuid_changed(current);
-       proc_id_connector(current, PROC_EVENT_UID);
+       new->fsuid = new->euid = uid;
+
+       retval = security_task_fix_setuid(new, old, LSM_SETID_ID);
+       if (retval < 0)
+               goto error;
 
-       return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID);
+       return commit_creds(new);
+
+error:
+       abort_creds(new);
+       return retval;
 }
 
 
@@ -692,54 +706,63 @@ asmlinkage long sys_setuid(uid_t uid)
  */
 asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
 {
-       int old_ruid = current->uid;
-       int old_euid = current->euid;
-       int old_suid = current->suid;
+       const struct cred *old;
+       struct cred *new;
        int retval;
 
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+
        retval = security_task_setuid(ruid, euid, suid, LSM_SETID_RES);
        if (retval)
-               return retval;
+               goto error;
+       old = current_cred();
 
+       retval = -EPERM;
        if (!capable(CAP_SETUID)) {
-               if ((ruid != (uid_t) -1) && (ruid != current->uid) &&
-                   (ruid != current->euid) && (ruid != current->suid))
-                       return -EPERM;
-               if ((euid != (uid_t) -1) && (euid != current->uid) &&
-                   (euid != current->euid) && (euid != current->suid))
-                       return -EPERM;
-               if ((suid != (uid_t) -1) && (suid != current->uid) &&
-                   (suid != current->euid) && (suid != current->suid))
-                       return -EPERM;
+               if (ruid != (uid_t) -1 && ruid != old->uid &&
+                   ruid != old->euid  && ruid != old->suid)
+                       goto error;
+               if (euid != (uid_t) -1 && euid != old->uid &&
+                   euid != old->euid  && euid != old->suid)
+                       goto error;
+               if (suid != (uid_t) -1 && suid != old->uid &&
+                   suid != old->euid  && suid != old->suid)
+                       goto error;
        }
+
+       retval = -EAGAIN;
        if (ruid != (uid_t) -1) {
-               if (ruid != current->uid && set_user(ruid, euid != current->euid) < 0)
-                       return -EAGAIN;
+               new->uid = ruid;
+               if (ruid != old->uid && set_user(new) < 0)
+                       goto error;
        }
-       if (euid != (uid_t) -1) {
-               if (euid != current->euid) {
-                       set_dumpable(current->mm, suid_dumpable);
-                       smp_wmb();
-               }
-               current->euid = euid;
-       }
-       current->fsuid = current->euid;
+       if (euid != (uid_t) -1)
+               new->euid = euid;
        if (suid != (uid_t) -1)
-               current->suid = suid;
+               new->suid = suid;
+       new->fsuid = new->euid;
+
+       retval = security_task_fix_setuid(new, old, LSM_SETID_RES);
+       if (retval < 0)
+               goto error;
 
-       key_fsuid_changed(current);
-       proc_id_connector(current, PROC_EVENT_UID);
+       return commit_creds(new);
 
-       return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RES);
+error:
+       abort_creds(new);
+       return retval;
 }
 
 asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __user *suid)
 {
+       const struct cred *cred = current_cred();
        int retval;
 
-       if (!(retval = put_user(current->uid, ruid)) &&
-           !(retval = put_user(current->euid, euid)))
-               retval = put_user(current->suid, suid);
+       if (!(retval   = put_user(cred->uid,  ruid)) &&
+           !(retval   = put_user(cred->euid, euid)))
+               retval = put_user(cred->suid, suid);
 
        return retval;
 }
@@ -749,48 +772,55 @@ asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __us
  */
 asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
 {
+       const struct cred *old;
+       struct cred *new;
        int retval;
 
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+       old = current_cred();
+
        retval = security_task_setgid(rgid, egid, sgid, LSM_SETID_RES);
        if (retval)
-               return retval;
+               goto error;
 
+       retval = -EPERM;
        if (!capable(CAP_SETGID)) {
-               if ((rgid != (gid_t) -1) && (rgid != current->gid) &&
-                   (rgid != current->egid) && (rgid != current->sgid))
-                       return -EPERM;
-               if ((egid != (gid_t) -1) && (egid != current->gid) &&
-                   (egid != current->egid) && (egid != current->sgid))
-                       return -EPERM;
-               if ((sgid != (gid_t) -1) && (sgid != current->gid) &&
-                   (sgid != current->egid) && (sgid != current->sgid))
-                       return -EPERM;
+               if (rgid != (gid_t) -1 && rgid != old->gid &&
+                   rgid != old->egid  && rgid != old->sgid)
+                       goto error;
+               if (egid != (gid_t) -1 && egid != old->gid &&
+                   egid != old->egid  && egid != old->sgid)
+                       goto error;
+               if (sgid != (gid_t) -1 && sgid != old->gid &&
+                   sgid != old->egid  && sgid != old->sgid)
+                       goto error;
        }
-       if (egid != (gid_t) -1) {
-               if (egid != current->egid) {
-                       set_dumpable(current->mm, suid_dumpable);
-                       smp_wmb();
-               }
-               current->egid = egid;
-       }
-       current->fsgid = current->egid;
+
        if (rgid != (gid_t) -1)
-               current->gid = rgid;
+               new->gid = rgid;
+       if (egid != (gid_t) -1)
+               new->egid = egid;
        if (sgid != (gid_t) -1)
-               current->sgid = sgid;
+               new->sgid = sgid;
+       new->fsgid = new->egid;
 
-       key_fsgid_changed(current);
-       proc_id_connector(current, PROC_EVENT_GID);
-       return 0;
+       return commit_creds(new);
+
+error:
+       abort_creds(new);
+       return retval;
 }
 
 asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __user *sgid)
 {
+       const struct cred *cred = current_cred();
        int retval;
 
-       if (!(retval = put_user(current->gid, rgid)) &&
-           !(retval = put_user(current->egid, egid)))
-               retval = put_user(current->sgid, sgid);
+       if (!(retval   = put_user(cred->gid,  rgid)) &&
+           !(retval   = put_user(cred->egid, egid)))
+               retval = put_user(cred->sgid, sgid);
 
        return retval;
 }
@@ -804,27 +834,35 @@ asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __us
  */
 asmlinkage long sys_setfsuid(uid_t uid)
 {
-       int old_fsuid;
+       const struct cred *old;
+       struct cred *new;
+       uid_t old_fsuid;
 
-       old_fsuid = current->fsuid;
-       if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS))
-               return old_fsuid;
+       new = prepare_creds();
+       if (!new)
+               return current_fsuid();
+       old = current_cred();
+       old_fsuid = old->fsuid;
 
-       if (uid == current->uid || uid == current->euid ||
-           uid == current->suid || uid == current->fsuid || 
+       if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS) < 0)
+               goto error;
+
+       if (uid == old->uid  || uid == old->euid  ||
+           uid == old->suid || uid == old->fsuid ||
            capable(CAP_SETUID)) {
                if (uid != old_fsuid) {
-                       set_dumpable(current->mm, suid_dumpable);
-                       smp_wmb();
+                       new->fsuid = uid;
+                       if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0)
+                               goto change_okay;
                }
-               current->fsuid = uid;
        }
 
-       key_fsuid_changed(current);
-       proc_id_connector(current, PROC_EVENT_UID);
-
-       security_task_post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS);
+error:
+       abort_creds(new);
+       return old_fsuid;
 
+change_okay:
+       commit_creds(new);
        return old_fsuid;
 }
 
@@ -833,23 +871,34 @@ asmlinkage long sys_setfsuid(uid_t uid)
  */
 asmlinkage long sys_setfsgid(gid_t gid)
 {
-       int old_fsgid;
+       const struct cred *old;
+       struct cred *new;
+       gid_t old_fsgid;
+
+       new = prepare_creds();
+       if (!new)
+               return current_fsgid();
+       old = current_cred();
+       old_fsgid = old->fsgid;
 
-       old_fsgid = current->fsgid;
        if (security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_FS))
-               return old_fsgid;
+               goto error;
 
-       if (gid == current->gid || gid == current->egid ||
-           gid == current->sgid || gid == current->fsgid || 
+       if (gid == old->gid  || gid == old->egid  ||
+           gid == old->sgid || gid == old->fsgid ||
            capable(CAP_SETGID)) {
                if (gid != old_fsgid) {
-                       set_dumpable(current->mm, suid_dumpable);
-                       smp_wmb();
+                       new->fsgid = gid;
+                       goto change_okay;
                }
-               current->fsgid = gid;
-               key_fsgid_changed(current);
-               proc_id_connector(current, PROC_EVENT_GID);
        }
+
+error:
+       abort_creds(new);
+       return old_fsgid;
+
+change_okay:
+       commit_creds(new);
        return old_fsgid;
 }
 
@@ -1118,7 +1167,7 @@ EXPORT_SYMBOL(groups_free);
 
 /* export the group_info to a user-space array */
 static int groups_to_user(gid_t __user *grouplist,
-    struct group_info *group_info)
+                         const struct group_info *group_info)
 {
        int i;
        unsigned int count = group_info->ngroups;
@@ -1186,7 +1235,7 @@ static void groups_sort(struct group_info *group_info)
 }
 
 /* a simple bsearch */
-int groups_search(struct group_info *group_info, gid_t grp)
+int groups_search(const struct group_info *group_info, gid_t grp)
 {
        unsigned int left, right;
 
@@ -1208,51 +1257,74 @@ int groups_search(struct group_info *group_info, gid_t grp)
        return 0;
 }
 
-/* validate and set current->group_info */
-int set_current_groups(struct group_info *group_info)
+/**
+ * set_groups - Change a group subscription in a set of credentials
+ * @new: The newly prepared set of credentials to alter
+ * @group_info: The group list to install
+ *
+ * Validate a group subscription and, if valid, insert it into a set
+ * of credentials.
+ */
+int set_groups(struct cred *new, struct group_info *group_info)
 {
        int retval;
-       struct group_info *old_info;
 
        retval = security_task_setgroups(group_info);
        if (retval)
                return retval;
 
+       put_group_info(new->group_info);
        groups_sort(group_info);
        get_group_info(group_info);
+       new->group_info = group_info;
+       return 0;
+}
+
+EXPORT_SYMBOL(set_groups);
 
-       task_lock(current);
-       old_info = current->group_info;
-       current->group_info = group_info;
-       task_unlock(current);
+/**
+ * set_current_groups - Change current's group subscription
+ * @group_info: The group list to impose
+ *
+ * Validate a group subscription and, if valid, impose it upon current's task
+ * security record.
+ */
+int set_current_groups(struct group_info *group_info)
+{
+       struct cred *new;
+       int ret;
 
-       put_group_info(old_info);
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
 
-       return 0;
+       ret = set_groups(new, group_info);
+       if (ret < 0) {
+               abort_creds(new);
+               return ret;
+       }
+
+       return commit_creds(new);
 }
 
 EXPORT_SYMBOL(set_current_groups);
 
 asmlinkage long sys_getgroups(int gidsetsize, gid_t __user *grouplist)
 {
-       int i = 0;
-
-       /*
-        *      SMP: Nobody else can change our grouplist. Thus we are
-        *      safe.
-        */
+       const struct cred *cred = current_cred();
+       int i;
 
        if (gidsetsize < 0)
                return -EINVAL;
 
        /* no need to grab task_lock here; it cannot change */
-       i = current->group_info->ngroups;
+       i = cred->group_info->ngroups;
        if (gidsetsize) {
                if (i > gidsetsize) {
                        i = -EINVAL;
                        goto out;
                }
-               if (groups_to_user(grouplist, current->group_info)) {
+               if (groups_to_user(grouplist, cred->group_info)) {
                        i = -EFAULT;
                        goto out;
                }
@@ -1296,9 +1368,11 @@ asmlinkage long sys_setgroups(int gidsetsize, gid_t __user *grouplist)
  */
 int in_group_p(gid_t grp)
 {
+       const struct cred *cred = current_cred();
        int retval = 1;
-       if (grp != current->fsgid)
-               retval = groups_search(current->group_info, grp);
+
+       if (grp != cred->fsgid)
+               retval = groups_search(cred->group_info, grp);
        return retval;
 }
 
@@ -1306,9 +1380,11 @@ EXPORT_SYMBOL(in_group_p);
 
 int in_egroup_p(gid_t grp)
 {
+       const struct cred *cred = current_cred();
        int retval = 1;
-       if (grp != current->egid)
-               retval = groups_search(current->group_info, grp);
+
+       if (grp != cred->egid)
+               retval = groups_search(cred->group_info, grp);
        return retval;
 }
 
@@ -1624,50 +1700,56 @@ asmlinkage long sys_umask(int mask)
 asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
                          unsigned long arg4, unsigned long arg5)
 {
-       long error = 0;
+       struct task_struct *me = current;
+       unsigned char comm[sizeof(me->comm)];
+       long error;
 
-       if (security_task_prctl(option, arg2, arg3, arg4, arg5, &error))
+       error = security_task_prctl(option, arg2, arg3, arg4, arg5);
+       if (error != -ENOSYS)
                return error;
 
+       error = 0;
        switch (option) {
                case PR_SET_PDEATHSIG:
                        if (!valid_signal(arg2)) {
                                error = -EINVAL;
                                break;
                        }
-                       current->pdeath_signal = arg2;
+                       me->pdeath_signal = arg2;
+                       error = 0;
                        break;
                case PR_GET_PDEATHSIG:
-                       error = put_user(current->pdeath_signal, (int __user *)arg2);
+                       error = put_user(me->pdeath_signal, (int __user *)arg2);
                        break;
                case PR_GET_DUMPABLE:
-                       error = get_dumpable(current->mm);
+                       error = get_dumpable(me->mm);
                        break;
                case PR_SET_DUMPABLE:
                        if (arg2 < 0 || arg2 > 1) {
                                error = -EINVAL;
                                break;
                        }
-                       set_dumpable(current->mm, arg2);
+                       set_dumpable(me->mm, arg2);
+                       error = 0;
                        break;
 
                case PR_SET_UNALIGN:
-                       error = SET_UNALIGN_CTL(current, arg2);
+                       error = SET_UNALIGN_CTL(me, arg2);
                        break;
                case PR_GET_UNALIGN:
-                       error = GET_UNALIGN_CTL(current, arg2);
+                       error = GET_UNALIGN_CTL(me, arg2);
                        break;
                case PR_SET_FPEMU:
-                       error = SET_FPEMU_CTL(current, arg2);
+                       error = SET_FPEMU_CTL(me, arg2);
                        break;
                case PR_GET_FPEMU:
-                       error = GET_FPEMU_CTL(current, arg2);
+                       error = GET_FPEMU_CTL(me, arg2);
                        break;
                case PR_SET_FPEXC:
-                       error = SET_FPEXC_CTL(current, arg2);
+                       error = SET_FPEXC_CTL(me, arg2);
                        break;
                case PR_GET_FPEXC:
-                       error = GET_FPEXC_CTL(current, arg2);
+                       error = GET_FPEXC_CTL(me, arg2);
                        break;
                case PR_GET_TIMING:
                        error = PR_TIMING_STATISTICAL;
@@ -1675,33 +1757,28 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
                case PR_SET_TIMING:
                        if (arg2 != PR_TIMING_STATISTICAL)
                                error = -EINVAL;
+                       else
+                               error = 0;
                        break;
 
-               case PR_SET_NAME: {
-                       struct task_struct *me = current;
-                       unsigned char ncomm[sizeof(me->comm)];
-
-                       ncomm[sizeof(me->comm)-1] = 0;
-                       if (strncpy_from_user(ncomm, (char __user *)arg2,
-                                               sizeof(me->comm)-1) < 0)
+               case PR_SET_NAME:
+                       comm[sizeof(me->comm)-1] = 0;
+                       if (strncpy_from_user(comm, (char __user *)arg2,
+                                             sizeof(me->comm) - 1) < 0)
                                return -EFAULT;
-                       set_task_comm(me, ncomm);
+                       set_task_comm(me, comm);
                        return 0;
-               }
-               case PR_GET_NAME: {
-                       struct task_struct *me = current;
-                       unsigned char tcomm[sizeof(me->comm)];
-
-                       get_task_comm(tcomm, me);
-                       if (copy_to_user((char __user *)arg2, tcomm, sizeof(tcomm)))
+               case PR_GET_NAME:
+                       get_task_comm(comm, me);
+                       if (copy_to_user((char __user *)arg2, comm,
+                                        sizeof(comm)))
                                return -EFAULT;
                        return 0;
-               }
                case PR_GET_ENDIAN:
-                       error = GET_ENDIAN(current, arg2);
+                       error = GET_ENDIAN(me, arg2);
                        break;
                case PR_SET_ENDIAN:
-                       error = SET_ENDIAN(current, arg2);
+                       error = SET_ENDIAN(me, arg2);
                        break;
 
                case PR_GET_SECCOMP:
@@ -1725,6 +1802,7 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
                                        current->default_timer_slack_ns;
                        else
                                current->timer_slack_ns = arg2;
+                       error = 0;
                        break;
                default:
                        error = -EINVAL;
index 3d56fe7..9d52b57 100644 (file)
@@ -1651,7 +1651,7 @@ out:
 
 static int test_perm(int mode, int op)
 {
-       if (!current->euid)
+       if (!current_euid())
                mode >>= 6;
        else if (in_egroup_p(0))
                mode >>= 3;
index dbd50fa..566257d 100644 (file)
@@ -1192,25 +1192,25 @@ asmlinkage long sys_getppid(void)
 asmlinkage long sys_getuid(void)
 {
        /* Only we change this so SMP safe */
-       return current->uid;
+       return current_uid();
 }
 
 asmlinkage long sys_geteuid(void)
 {
        /* Only we change this so SMP safe */
-       return current->euid;
+       return current_euid();
 }
 
 asmlinkage long sys_getgid(void)
 {
        /* Only we change this so SMP safe */
-       return current->gid;
+       return current_gid();
 }
 
 asmlinkage long sys_getegid(void)
 {
        /* Only we change this so SMP safe */
-       return  current->egid;
+       return  current_egid();
 }
 
 #endif
index d86e325..1ee9e4e 100644 (file)
@@ -246,7 +246,7 @@ __update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu)
 
        memcpy(data->comm, tsk->comm, TASK_COMM_LEN);
        data->pid = tsk->pid;
-       data->uid = tsk->uid;
+       data->uid = task_uid(tsk);
        data->nice = tsk->static_prio - 20 - MAX_RT_PRIO;
        data->policy = tsk->policy;
        data->rt_priority = tsk->rt_priority;
index 8ebcd85..2dc06ab 100644 (file)
@@ -27,6 +27,7 @@
  */
 void bacct_add_tsk(struct taskstats *stats, struct task_struct *tsk)
 {
+       const struct cred *tcred;
        struct timespec uptime, ts;
        u64 ac_etime;
 
@@ -53,10 +54,11 @@ void bacct_add_tsk(struct taskstats *stats, struct task_struct *tsk)
                stats->ac_flag |= AXSIG;
        stats->ac_nice   = task_nice(tsk);
        stats->ac_sched  = tsk->policy;
-       stats->ac_uid    = tsk->uid;
-       stats->ac_gid    = tsk->gid;
        stats->ac_pid    = tsk->pid;
        rcu_read_lock();
+       tcred = __task_cred(tsk);
+       stats->ac_uid    = tcred->uid;
+       stats->ac_gid    = tcred->gid;
        stats->ac_ppid   = pid_alive(tsk) ?
                                rcu_dereference(tsk->real_parent)->tgid : 0;
        rcu_read_unlock();
index 3e41c16..2460c31 100644 (file)
@@ -84,11 +84,12 @@ asmlinkage long sys_setresuid16(old_uid_t ruid, old_uid_t euid, old_uid_t suid)
 
 asmlinkage long sys_getresuid16(old_uid_t __user *ruid, old_uid_t __user *euid, old_uid_t __user *suid)
 {
+       const struct cred *cred = current_cred();
        int retval;
 
-       if (!(retval = put_user(high2lowuid(current->uid), ruid)) &&
-           !(retval = put_user(high2lowuid(current->euid), euid)))
-               retval = put_user(high2lowuid(current->suid), suid);
+       if (!(retval   = put_user(high2lowuid(cred->uid),  ruid)) &&
+           !(retval   = put_user(high2lowuid(cred->euid), euid)))
+               retval = put_user(high2lowuid(cred->suid), suid);
 
        return retval;
 }
@@ -104,11 +105,12 @@ asmlinkage long sys_setresgid16(old_gid_t rgid, old_gid_t egid, old_gid_t sgid)
 
 asmlinkage long sys_getresgid16(old_gid_t __user *rgid, old_gid_t __user *egid, old_gid_t __user *sgid)
 {
+       const struct cred *cred = current_cred();
        int retval;
 
-       if (!(retval = put_user(high2lowgid(current->gid), rgid)) &&
-           !(retval = put_user(high2lowgid(current->egid), egid)))
-               retval = put_user(high2lowgid(current->sgid), sgid);
+       if (!(retval   = put_user(high2lowgid(cred->gid),  rgid)) &&
+           !(retval   = put_user(high2lowgid(cred->egid), egid)))
+               retval = put_user(high2lowgid(cred->sgid), sgid);
 
        return retval;
 }
@@ -161,25 +163,24 @@ static int groups16_from_user(struct group_info *group_info,
 
 asmlinkage long sys_getgroups16(int gidsetsize, old_gid_t __user *grouplist)
 {
-       int i = 0;
+       const struct cred *cred = current_cred();
+       int i;
 
        if (gidsetsize < 0)
                return -EINVAL;
 
-       get_group_info(current->group_info);
-       i = current->group_info->ngroups;
+       i = cred->group_info->ngroups;
        if (gidsetsize) {
                if (i > gidsetsize) {
                        i = -EINVAL;
                        goto out;
                }
-               if (groups16_to_user(grouplist, current->group_info)) {
+               if (groups16_to_user(grouplist, cred->group_info)) {
                        i = -EFAULT;
                        goto out;
                }
        }
 out:
-       put_group_info(current->group_info);
        return i;
 }
 
@@ -210,20 +211,20 @@ asmlinkage long sys_setgroups16(int gidsetsize, old_gid_t __user *grouplist)
 
 asmlinkage long sys_getuid16(void)
 {
-       return high2lowuid(current->uid);
+       return high2lowuid(current_uid());
 }
 
 asmlinkage long sys_geteuid16(void)
 {
-       return high2lowuid(current->euid);
+       return high2lowuid(current_euid());
 }
 
 asmlinkage long sys_getgid16(void)
 {
-       return high2lowgid(current->gid);
+       return high2lowgid(current_gid());
 }
 
 asmlinkage long sys_getegid16(void)
 {
-       return high2lowgid(current->egid);
+       return high2lowgid(current_egid());
 }
index 39d6159..6608a3d 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/user_namespace.h>
+#include "cred-internals.h"
 
 struct user_namespace init_user_ns = {
        .kref = {
-               .refcount       = ATOMIC_INIT(2),
+               .refcount       = ATOMIC_INIT(1),
        },
-       .root_user = &root_user,
+       .creator = &root_user,
 };
 EXPORT_SYMBOL_GPL(init_user_ns);
 
@@ -47,12 +48,14 @@ static struct kmem_cache *uid_cachep;
  */
 static DEFINE_SPINLOCK(uidhash_lock);
 
+/* root_user.__count is 2, 1 for init task cred, 1 for init_user_ns->creator */
 struct user_struct root_user = {
-       .__count        = ATOMIC_INIT(1),
+       .__count        = ATOMIC_INIT(2),
        .processes      = ATOMIC_INIT(1),
        .files          = ATOMIC_INIT(0),
        .sigpending     = ATOMIC_INIT(0),
        .locked_shm     = 0,
+       .user_ns        = &init_user_ns,
 #ifdef CONFIG_USER_SCHED
        .tg             = &init_task_group,
 #endif
@@ -104,16 +107,10 @@ static int sched_create_user(struct user_struct *up)
        return rc;
 }
 
-static void sched_switch_user(struct task_struct *p)
-{
-       sched_move_task(p);
-}
-
 #else  /* CONFIG_USER_SCHED */
 
 static void sched_destroy_user(struct user_struct *up) { }
 static int sched_create_user(struct user_struct *up) { return 0; }
-static void sched_switch_user(struct task_struct *p) { }
 
 #endif /* CONFIG_USER_SCHED */
 
@@ -242,13 +239,21 @@ static struct kobj_type uids_ktype = {
        .release = uids_release,
 };
 
-/* create /sys/kernel/uids/<uid>/cpu_share file for this user */
+/*
+ * Create /sys/kernel/uids/<uid>/cpu_share file for this user
+ * We do not create this file for users in a user namespace (until
+ * sysfs tagging is implemented).
+ *
+ * See Documentation/scheduler/sched-design-CFS.txt for ramifications.
+ */
 static int uids_user_create(struct user_struct *up)
 {
        struct kobject *kobj = &up->kobj;
        int error;
 
        memset(kobj, 0, sizeof(struct kobject));
+       if (up->user_ns != &init_user_ns)
+               return 0;
        kobj->kset = uids_kset;
        error = kobject_init_and_add(kobj, &uids_ktype, NULL, "%d", up->uid);
        if (error) {
@@ -284,6 +289,8 @@ static void remove_user_sysfs_dir(struct work_struct *w)
        unsigned long flags;
        int remove_user = 0;
 
+       if (up->user_ns != &init_user_ns)
+               return;
        /* Make uid_hash_remove() + sysfs_remove_file() + kobject_del()
         * atomic.
         */
@@ -319,12 +326,13 @@ done:
  * IRQ state (as stored in flags) is restored and uidhash_lock released
  * upon function exit.
  */
-static inline void free_user(struct user_struct *up, unsigned long flags)
+static void free_user(struct user_struct *up, unsigned long flags)
 {
        /* restore back the count */
        atomic_inc(&up->__count);
        spin_unlock_irqrestore(&uidhash_lock, flags);
 
+       put_user_ns(up->user_ns);
        INIT_WORK(&up->work, remove_user_sysfs_dir);
        schedule_work(&up->work);
 }
@@ -340,13 +348,14 @@ static inline void uids_mutex_unlock(void) { }
  * IRQ state (as stored in flags) is restored and uidhash_lock released
  * upon function exit.
  */
-static inline void free_user(struct user_struct *up, unsigned long flags)
+static void free_user(struct user_struct *up, unsigned long flags)
 {
        uid_hash_remove(up);
        spin_unlock_irqrestore(&uidhash_lock, flags);
        sched_destroy_user(up);
        key_put(up->uid_keyring);
        key_put(up->session_keyring);
+       put_user_ns(up->user_ns);
        kmem_cache_free(uid_cachep, up);
 }
 
@@ -362,7 +371,7 @@ struct user_struct *find_user(uid_t uid)
 {
        struct user_struct *ret;
        unsigned long flags;
-       struct user_namespace *ns = current->nsproxy->user_ns;
+       struct user_namespace *ns = current_user_ns();
 
        spin_lock_irqsave(&uidhash_lock, flags);
        ret = uid_hash_find(uid, uidhashentry(ns, uid));
@@ -409,6 +418,8 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid)
                if (sched_create_user(new) < 0)
                        goto out_free_user;
 
+               new->user_ns = get_user_ns(ns);
+
                if (uids_user_create(new))
                        goto out_destoy_sched;
 
@@ -432,7 +443,6 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid)
                        up = new;
                }
                spin_unlock_irq(&uidhash_lock);
-
        }
 
        uids_mutex_unlock();
@@ -441,6 +451,7 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid)
 
 out_destoy_sched:
        sched_destroy_user(new);
+       put_user_ns(new->user_ns);
 out_free_user:
        kmem_cache_free(uid_cachep, new);
 out_unlock:
@@ -448,63 +459,6 @@ out_unlock:
        return NULL;
 }
 
-void switch_uid(struct user_struct *new_user)
-{
-       struct user_struct *old_user;
-
-       /* What if a process setreuid()'s and this brings the
-        * new uid over his NPROC rlimit?  We can check this now
-        * cheaply with the new uid cache, so if it matters
-        * we should be checking for it.  -DaveM
-        */
-       old_user = current->user;
-       atomic_inc(&new_user->processes);
-       atomic_dec(&old_user->processes);
-       switch_uid_keyring(new_user);
-       current->user = new_user;
-       sched_switch_user(current);
-
-       /*
-        * We need to synchronize with __sigqueue_alloc()
-        * doing a get_uid(p->user).. If that saw the old
-        * user value, we need to wait until it has exited
-        * its critical region before we can free the old
-        * structure.
-        */
-       smp_mb();
-       spin_unlock_wait(&current->sighand->siglock);
-
-       free_uid(old_user);
-       suid_keys(current);
-}
-
-#ifdef CONFIG_USER_NS
-void release_uids(struct user_namespace *ns)
-{
-       int i;
-       unsigned long flags;
-       struct hlist_head *head;
-       struct hlist_node *nd;
-
-       spin_lock_irqsave(&uidhash_lock, flags);
-       /*
-        * collapse the chains so that the user_struct-s will
-        * be still alive, but not in hashes. subsequent free_uid()
-        * will free them.
-        */
-       for (i = 0; i < UIDHASH_SZ; i++) {
-               head = ns->uidhash_table + i;
-               while (!hlist_empty(head)) {
-                       nd = head->first;
-                       hlist_del_init(nd);
-               }
-       }
-       spin_unlock_irqrestore(&uidhash_lock, flags);
-
-       free_uid(ns->root_user);
-}
-#endif
-
 static int __init uid_cache_init(void)
 {
        int n;
index 532858f..7908431 100644 (file)
@@ -9,60 +9,55 @@
 #include <linux/nsproxy.h>
 #include <linux/slab.h>
 #include <linux/user_namespace.h>
+#include <linux/cred.h>
 
 /*
- * Clone a new ns copying an original user ns, setting refcount to 1
- * @old_ns: namespace to clone
- * Return NULL on error (failure to kmalloc), new ns otherwise
+ * Create a new user namespace, deriving the creator from the user in the
+ * passed credentials, and replacing that user with the new root user for the
+ * new namespace.
+ *
+ * This is called by copy_creds(), which will finish setting the target task's
+ * credentials.
  */
-static struct user_namespace *clone_user_ns(struct user_namespace *old_ns)
+int create_user_ns(struct cred *new)
 {
        struct user_namespace *ns;
-       struct user_struct *new_user;
+       struct user_struct *root_user;
        int n;
 
        ns = kmalloc(sizeof(struct user_namespace), GFP_KERNEL);
        if (!ns)
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
 
        kref_init(&ns->kref);
 
        for (n = 0; n < UIDHASH_SZ; ++n)
                INIT_HLIST_HEAD(ns->uidhash_table + n);
 
-       /* Insert new root user.  */
-       ns->root_user = alloc_uid(ns, 0);
-       if (!ns->root_user) {
+       /* Alloc new root user.  */
+       root_user = alloc_uid(ns, 0);
+       if (!root_user) {
                kfree(ns);
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
        }
 
-       /* Reset current->user with a new one */
-       new_user = alloc_uid(ns, current->uid);
-       if (!new_user) {
-               free_uid(ns->root_user);
-               kfree(ns);
-               return ERR_PTR(-ENOMEM);
-       }
-
-       switch_uid(new_user);
-       return ns;
-}
-
-struct user_namespace * copy_user_ns(int flags, struct user_namespace *old_ns)
-{
-       struct user_namespace *new_ns;
-
-       BUG_ON(!old_ns);
-       get_user_ns(old_ns);
-
-       if (!(flags & CLONE_NEWUSER))
-               return old_ns;
+       /* set the new root user in the credentials under preparation */
+       ns->creator = new->user;
+       new->user = root_user;
+       new->uid = new->euid = new->suid = new->fsuid = 0;
+       new->gid = new->egid = new->sgid = new->fsgid = 0;
+       put_group_info(new->group_info);
+       new->group_info = get_group_info(&init_groups);
+#ifdef CONFIG_KEYS
+       key_put(new->request_key_auth);
+       new->request_key_auth = NULL;
+#endif
+       /* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */
 
-       new_ns = clone_user_ns(old_ns);
+       /* alloc_uid() incremented the userns refcount.  Just set it to 1 */
+       kref_set(&ns->kref, 1);
 
-       put_user_ns(old_ns);
-       return new_ns;
+       return 0;
 }
 
 void free_user_ns(struct kref *kref)
@@ -70,7 +65,7 @@ void free_user_ns(struct kref *kref)
        struct user_namespace *ns;
 
        ns = container_of(kref, struct user_namespace, kref);
-       release_uids(ns);
+       free_uid(ns->creator);
        kfree(ns);
 }
 EXPORT_SYMBOL(free_user_ns);
index d4dc69d..4952322 100644 (file)
@@ -84,21 +84,21 @@ static cpumask_t cpu_singlethread_map __read_mostly;
 static cpumask_t cpu_populated_map __read_mostly;
 
 /* If it's single threaded, it isn't in the list of workqueues. */
-static inline int is_single_threaded(struct workqueue_struct *wq)
+static inline int is_wq_single_threaded(struct workqueue_struct *wq)
 {
        return wq->singlethread;
 }
 
 static const cpumask_t *wq_cpu_map(struct workqueue_struct *wq)
 {
-       return is_single_threaded(wq)
+       return is_wq_single_threaded(wq)
                ? &cpu_singlethread_map : &cpu_populated_map;
 }
 
 static
 struct cpu_workqueue_struct *wq_per_cpu(struct workqueue_struct *wq, int cpu)
 {
-       if (unlikely(is_single_threaded(wq)))
+       if (unlikely(is_wq_single_threaded(wq)))
                cpu = singlethread_cpu;
        return per_cpu_ptr(wq->cpu_wq, cpu);
 }
@@ -769,7 +769,7 @@ static int create_workqueue_thread(struct cpu_workqueue_struct *cwq, int cpu)
 {
        struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
        struct workqueue_struct *wq = cwq->wq;
-       const char *fmt = is_single_threaded(wq) ? "%s" : "%s/%d";
+       const char *fmt = is_wq_single_threaded(wq) ? "%s" : "%s/%d";
        struct task_struct *p;
 
        p = kthread_create(worker_thread, cwq, fmt, wq->name, cpu);
index 85cf7ea..fd4118e 100644 (file)
@@ -64,6 +64,8 @@ config CRC7
 
 config LIBCRC32C
        tristate "CRC32c (Castagnoli, et al) Cyclic Redundancy-Check"
+       select CRYPTO
+       select CRYPTO_CRC32C
        help
          This option is provided for the case where no in-kernel-tree
          modules require CRC32c functions, but a module built outside the
index 7cb65d8..80fe8a3 100644 (file)
@@ -11,7 +11,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
         rbtree.o radix-tree.o dump_stack.o \
         idr.o int_sqrt.o extable.o prio_tree.o \
         sha1.o irq_regs.o reciprocal_div.o argv_split.o \
-        proportions.o prio_heap.o ratelimit.o show_mem.o
+        proportions.o prio_heap.o ratelimit.o show_mem.o is_single_threaded.o
 
 lib-$(CONFIG_MMU) += ioremap.o
 lib-$(CONFIG_SMP) += cpumask.o
diff --git a/lib/is_single_threaded.c b/lib/is_single_threaded.c
new file mode 100644 (file)
index 0000000..f1ed2fe
--- /dev/null
@@ -0,0 +1,45 @@
+/* Function to determine if a thread group is single threaded or not
+ *
+ * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from security/selinux/hooks.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/sched.h>
+
+/**
+ * is_single_threaded - Determine if a thread group is single-threaded or not
+ * @p: A task in the thread group in question
+ *
+ * This returns true if the thread group to which a task belongs is single
+ * threaded, false if it is not.
+ */
+bool is_single_threaded(struct task_struct *p)
+{
+       struct task_struct *g, *t;
+       struct mm_struct *mm = p->mm;
+
+       if (atomic_read(&p->signal->count) != 1)
+               goto no;
+
+       if (atomic_read(&p->mm->mm_users) != 1) {
+               read_lock(&tasklist_lock);
+               do_each_thread(g, t) {
+                       if (t->mm == mm && t != p)
+                               goto no_unlock;
+               } while_each_thread(g, t);
+               read_unlock(&tasklist_lock);
+       }
+
+       return true;
+
+no_unlock:
+       read_unlock(&tasklist_lock);
+no:
+       return false;
+}
index b5c3287..244f548 100644 (file)
  * any later version.
  *
  */
-#include <linux/crc32c.h>
-#include <linux/compiler.h>
-#include <linux/module.h>
-
-MODULE_AUTHOR("Clay Haapala <chaapala@cisco.com>");
-MODULE_DESCRIPTION("CRC32c (Castagnoli) calculations");
-MODULE_LICENSE("GPL");
 
-#define CRC32C_POLY_BE 0x1EDC6F41
-#define CRC32C_POLY_LE 0x82F63B78
+#include <crypto/hash.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 
-#ifndef CRC_LE_BITS 
-# define CRC_LE_BITS 8
-#endif
+static struct crypto_shash *tfm;
 
+u32 crc32c(u32 crc, const void *address, unsigned int length)
+{
+       struct {
+               struct shash_desc shash;
+               char ctx[crypto_shash_descsize(tfm)];
+       } desc;
+       int err;
 
-/*
- * Haven't generated a big-endian table yet, but the bit-wise version
- * should at least work.
- */
-#if defined CRC_BE_BITS && CRC_BE_BITS != 1
-#undef CRC_BE_BITS
-#endif
-#ifndef CRC_BE_BITS
-# define CRC_BE_BITS 1
-#endif
+       desc.shash.tfm = tfm;
+       desc.shash.flags = 0;
+       *(u32 *)desc.ctx = crc;
 
-EXPORT_SYMBOL(crc32c_le);
+       err = crypto_shash_update(&desc.shash, address, length);
+       BUG_ON(err);
 
-#if CRC_LE_BITS == 1
-/*
- * Compute things bit-wise, as done in crc32.c.  We could share the tight 
- * loop below with crc32 and vary the POLY if we don't find value in terms
- * of space and maintainability in keeping the two modules separate.
- */
-u32 __pure
-crc32c_le(u32 crc, unsigned char const *p, size_t len)
-{
-       int i;
-       while (len--) {
-               crc ^= *p++;
-               for (i = 0; i < 8; i++)
-                       crc = (crc >> 1) ^ ((crc & 1) ? CRC32C_POLY_LE : 0);
-       }
-       return crc;
+       return *(u32 *)desc.ctx;
 }
-#else
-
-/*
- * This is the CRC-32C table
- * Generated with:
- * width = 32 bits
- * poly = 0x1EDC6F41
- * reflect input bytes = true
- * reflect output bytes = true
- */
-
-static const u32 crc32c_table[256] = {
-       0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
-       0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
-       0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
-       0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
-       0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
-       0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
-       0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
-       0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
-       0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
-       0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
-       0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
-       0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
-       0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
-       0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
-       0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
-       0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
-       0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
-       0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
-       0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
-       0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
-       0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
-       0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
-       0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
-       0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
-       0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
-       0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
-       0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
-       0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
-       0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
-       0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
-       0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
-       0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
-       0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
-       0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
-       0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
-       0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
-       0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
-       0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
-       0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
-       0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
-       0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
-       0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
-       0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
-       0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
-       0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
-       0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
-       0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
-       0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
-       0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
-       0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
-       0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
-       0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
-       0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
-       0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
-       0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
-       0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
-       0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
-       0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
-       0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
-       0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
-       0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
-       0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
-       0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
-       0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
-};
 
-/*
- * Steps through buffer one byte at at time, calculates reflected 
- * crc using table.
- */
+EXPORT_SYMBOL(crc32c);
 
-u32 __pure
-crc32c_le(u32 crc, unsigned char const *data, size_t length)
+static int __init libcrc32c_mod_init(void)
 {
-       while (length--)
-               crc =
-                   crc32c_table[(crc ^ *data++) & 0xFFL] ^ (crc >> 8);
+       tfm = crypto_alloc_shash("crc32c", 0, 0);
+       if (IS_ERR(tfm))
+               return PTR_ERR(tfm);
 
-       return crc;
+       return 0;
 }
 
-#endif /* CRC_LE_BITS == 8 */
-
-EXPORT_SYMBOL(crc32c_be);
-
-#if CRC_BE_BITS == 1
-u32 __pure
-crc32c_be(u32 crc, unsigned char const *p, size_t len)
+static void __exit libcrc32c_mod_fini(void)
 {
-       int i;
-       while (len--) {
-               crc ^= *p++ << 24;
-               for (i = 0; i < 8; i++)
-                       crc =
-                           (crc << 1) ^ ((crc & 0x80000000) ? CRC32C_POLY_BE :
-                                         0);
-       }
-       return crc;
+       crypto_free_shash(tfm);
 }
-#endif
 
-/*
- * Unit test
- *
- * A small unit test suite is implemented as part of the crypto suite.
- * Select CRYPTO_CRC32C and use the tcrypt module to run the tests.
- */
+module_init(libcrc32c_mod_init);
+module_exit(libcrc32c_mod_fini);
+
+MODULE_AUTHOR("Clay Haapala <chaapala@cisco.com>");
+MODULE_DESCRIPTION("CRC32c (Castagnoli) calculations");
+MODULE_LICENSE("GPL");
index e9493b1..e412ffa 100644 (file)
@@ -1114,6 +1114,7 @@ asmlinkage long sys_migrate_pages(pid_t pid, unsigned long maxnode,
                const unsigned long __user *old_nodes,
                const unsigned long __user *new_nodes)
 {
+       const struct cred *cred = current_cred(), *tcred;
        struct mm_struct *mm;
        struct task_struct *task;
        nodemask_t old;
@@ -1148,12 +1149,16 @@ asmlinkage long sys_migrate_pages(pid_t pid, unsigned long maxnode,
         * capabilities, superuser privileges or the same
         * userid as the target process.
         */
-       if ((current->euid != task->suid) && (current->euid != task->uid) &&
-           (current->uid != task->suid) && (current->uid != task->uid) &&
+       rcu_read_lock();
+       tcred = __task_cred(task);
+       if (cred->euid != tcred->suid && cred->euid != tcred->uid &&
+           cred->uid  != tcred->suid && cred->uid  != tcred->uid &&
            !capable(CAP_SYS_NICE)) {
+               rcu_read_unlock();
                err = -EPERM;
                goto out;
        }
+       rcu_read_unlock();
 
        task_nodes = cpuset_mems_allowed(task);
        /* Is the user allowed to access the target nodes? */
index 037b096..21631ab 100644 (file)
@@ -1075,6 +1075,7 @@ asmlinkage long sys_move_pages(pid_t pid, unsigned long nr_pages,
                        const int __user *nodes,
                        int __user *status, int flags)
 {
+       const struct cred *cred = current_cred(), *tcred;
        struct task_struct *task;
        struct mm_struct *mm;
        int err;
@@ -1105,12 +1106,16 @@ asmlinkage long sys_move_pages(pid_t pid, unsigned long nr_pages,
         * capabilities, superuser privileges or the same
         * userid as the target process.
         */
-       if ((current->euid != task->suid) && (current->euid != task->uid) &&
-           (current->uid != task->suid) && (current->uid != task->uid) &&
+       rcu_read_lock();
+       tcred = __task_cred(task);
+       if (cred->euid != tcred->suid && cred->euid != tcred->uid &&
+           cred->uid  != tcred->suid && cred->uid  != tcred->uid &&
            !capable(CAP_SYS_NICE)) {
+               rcu_read_unlock();
                err = -EPERM;
                goto out;
        }
+       rcu_read_unlock();
 
        err = security_task_movememory(task);
        if (err)
index a0a0190..558f9af 100644 (file)
@@ -128,8 +128,8 @@ unsigned long badness(struct task_struct *p, unsigned long uptime)
         * Superuser processes are usually more important, so we make it
         * less likely that we kill those.
         */
-       if (has_capability(p, CAP_SYS_ADMIN) ||
-           has_capability(p, CAP_SYS_RESOURCE))
+       if (has_capability_noaudit(p, CAP_SYS_ADMIN) ||
+           has_capability_noaudit(p, CAP_SYS_RESOURCE))
                points /= 4;
 
        /*
@@ -138,7 +138,7 @@ unsigned long badness(struct task_struct *p, unsigned long uptime)
         * tend to only have this flag set on applications they think
         * of as important.
         */
-       if (has_capability(p, CAP_SYS_RAWIO))
+       if (has_capability_noaudit(p, CAP_SYS_RAWIO))
                points /= 4;
 
        /*
@@ -299,9 +299,9 @@ static void dump_tasks(const struct mem_cgroup *mem)
 
                task_lock(p);
                printk(KERN_INFO "[%5d] %5d %5d %8lu %8lu %3d     %3d %s\n",
-                      p->pid, p->uid, p->tgid, p->mm->total_vm,
-                      get_mm_rss(p->mm), (int)task_cpu(p), p->oomkilladj,
-                      p->comm);
+                      p->pid, __task_cred(p)->uid, p->tgid,
+                      p->mm->total_vm, get_mm_rss(p->mm), (int)task_cpu(p),
+                      p->oomkilladj, p->comm);
                task_unlock(p);
        } while_each_thread(g, p);
 }
index 0ed0752..f1b0d48 100644 (file)
@@ -1513,8 +1513,8 @@ shmem_get_inode(struct super_block *sb, int mode, dev_t dev)
        inode = new_inode(sb);
        if (inode) {
                inode->i_mode = mode;
-               inode->i_uid = current->fsuid;
-               inode->i_gid = current->fsgid;
+               inode->i_uid = current_fsuid();
+               inode->i_gid = current_fsgid();
                inode->i_blocks = 0;
                inode->i_mapping->backing_dev_info = &shmem_backing_dev_info;
                inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
@@ -2278,8 +2278,8 @@ static int shmem_fill_super(struct super_block *sb,
        sbinfo->max_blocks = 0;
        sbinfo->max_inodes = 0;
        sbinfo->mode = S_IRWXUGO | S_ISVTX;
-       sbinfo->uid = current->fsuid;
-       sbinfo->gid = current->fsgid;
+       sbinfo->uid = current_fsuid();
+       sbinfo->gid = current_fsgid();
        sbinfo->mpol = NULL;
        sb->s_fs_info = sbinfo;
 
index 4b52945..821f1ec 100644 (file)
@@ -627,7 +627,7 @@ static struct p9_fid *p9_fid_create(struct p9_client *clnt)
        memset(&fid->qid, 0, sizeof(struct p9_qid));
        fid->mode = -1;
        fid->rdir_fpos = 0;
-       fid->uid = current->fsuid;
+       fid->uid = current_fsuid();
        fid->clnt = clnt;
        fid->aux = NULL;
 
index 28c7157..00d9e5e 100644 (file)
@@ -1045,7 +1045,7 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
        if (addr->fsa_ax25.sax25_family != AF_AX25)
                return -EINVAL;
 
-       user = ax25_findbyuid(current->euid);
+       user = ax25_findbyuid(current_euid());
        if (user) {
                call = user->call;
                ax25_uid_put(user);
index 8672cd8..c833ba4 100644 (file)
@@ -421,7 +421,7 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
                goto put;
        }
 
-       user = ax25_findbyuid(current->euid);
+       user = ax25_findbyuid(current_euid());
        if (user) {
                ax25->source_addr = user->call;
                ax25_uid_put(user);
index 9174c77..89912ae 100644 (file)
@@ -2961,6 +2961,8 @@ static void dev_change_rx_flags(struct net_device *dev, int flags)
 static int __dev_set_promiscuity(struct net_device *dev, int inc)
 {
        unsigned short old_flags = dev->flags;
+       uid_t uid;
+       gid_t gid;
 
        ASSERT_RTNL();
 
@@ -2985,15 +2987,17 @@ static int __dev_set_promiscuity(struct net_device *dev, int inc)
                printk(KERN_INFO "device %s %s promiscuous mode\n",
                       dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
                                                               "left");
-               if (audit_enabled)
+               if (audit_enabled) {
+                       current_uid_gid(&uid, &gid);
                        audit_log(current->audit_context, GFP_ATOMIC,
                                AUDIT_ANOM_PROMISCUOUS,
                                "dev=%s prom=%d old_prom=%d auid=%u uid=%u gid=%u ses=%u",
                                dev->name, (dev->flags & IFF_PROMISC),
                                (old_flags & IFF_PROMISC),
                                audit_get_loginuid(current),
-                               current->uid, current->gid,
+                               uid, gid,
                                audit_get_sessionid(current));
+               }
 
                dev_change_rx_flags(dev, IFF_PROMISC);
        }
index b12303d..b7ba91b 100644 (file)
 
 static __inline__ int scm_check_creds(struct ucred *creds)
 {
+       const struct cred *cred = current_cred();
+
        if ((creds->pid == task_tgid_vnr(current) || capable(CAP_SYS_ADMIN)) &&
-           ((creds->uid == current->uid || creds->uid == current->euid ||
-             creds->uid == current->suid) || capable(CAP_SETUID)) &&
-           ((creds->gid == current->gid || creds->gid == current->egid ||
-             creds->gid == current->sgid) || capable(CAP_SETGID))) {
+           ((creds->uid == cred->uid   || creds->uid == cred->euid ||
+             creds->uid == cred->suid) || capable(CAP_SETUID)) &&
+           ((creds->gid == cred->gid   || creds->gid == cred->egid ||
+             creds->gid == cred->sgid) || capable(CAP_SETGID))) {
               return 0;
        }
        return -EPERM;
index fc6ce04..7b5dbe1 100644 (file)
@@ -340,8 +340,8 @@ static void dump_packet(const struct nf_loginfo *info,
                read_lock_bh(&skb->sk->sk_callback_lock);
                if (skb->sk->sk_socket && skb->sk->sk_socket->file)
                        printk("UID=%u GID=%u ",
-                               skb->sk->sk_socket->file->f_uid,
-                               skb->sk->sk_socket->file->f_gid);
+                               skb->sk->sk_socket->file->f_cred->fsuid,
+                               skb->sk->sk_socket->file->f_cred->fsgid);
                read_unlock_bh(&skb->sk->sk_callback_lock);
        }
 
index 37a4e77..bd3c7b9 100644 (file)
@@ -388,7 +388,7 @@ fl_create(struct net *net, struct in6_flowlabel_req *freq, char __user *optval,
                fl->owner = current->pid;
                break;
        case IPV6_FL_S_USER:
-               fl->owner = current->euid;
+               fl->owner = current_euid();
                break;
        default:
                err = -EINVAL;
index caa441d..871d157 100644 (file)
@@ -364,8 +364,8 @@ static void dump_packet(const struct nf_loginfo *info,
                read_lock_bh(&skb->sk->sk_callback_lock);
                if (skb->sk->sk_socket && skb->sk->sk_socket->file)
                        printk("UID=%u GID=%u ",
-                               skb->sk->sk_socket->file->f_uid,
-                               skb->sk->sk_socket->file->f_gid);
+                               skb->sk->sk_socket->file->f_cred->fsuid,
+                               skb->sk->sk_socket->file->f_cred->fsgid);
                read_unlock_bh(&skb->sk->sk_callback_lock);
        }
 
index 41e0105..38f9efd 100644 (file)
@@ -474,8 +474,9 @@ __build_packet_message(struct nfulnl_instance *inst,
        if (skb->sk) {
                read_lock_bh(&skb->sk->sk_callback_lock);
                if (skb->sk->sk_socket && skb->sk->sk_socket->file) {
-                       __be32 uid = htonl(skb->sk->sk_socket->file->f_uid);
-                       __be32 gid = htonl(skb->sk->sk_socket->file->f_gid);
+                       struct file *file = skb->sk->sk_socket->file;
+                       __be32 uid = htonl(file->f_cred->fsuid);
+                       __be32 gid = htonl(file->f_cred->fsgid);
                        /* need to unlock here since NLA_PUT may goto */
                        read_unlock_bh(&skb->sk->sk_callback_lock);
                        NLA_PUT_BE32(inst->skb, NFULA_UID, uid);
index f19ebd9..22b2a5e 100644 (file)
@@ -34,12 +34,12 @@ owner_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par)
                return false;
 
        if (info->match & IPT_OWNER_UID)
-               if ((filp->f_uid != info->uid) ^
+               if ((filp->f_cred->fsuid != info->uid) ^
                    !!(info->invert & IPT_OWNER_UID))
                        return false;
 
        if (info->match & IPT_OWNER_GID)
-               if ((filp->f_gid != info->gid) ^
+               if ((filp->f_cred->fsgid != info->gid) ^
                    !!(info->invert & IPT_OWNER_GID))
                        return false;
 
@@ -60,12 +60,12 @@ owner_mt6_v0(const struct sk_buff *skb, const struct xt_match_param *par)
                return false;
 
        if (info->match & IP6T_OWNER_UID)
-               if ((filp->f_uid != info->uid) ^
+               if ((filp->f_cred->fsuid != info->uid) ^
                    !!(info->invert & IP6T_OWNER_UID))
                        return false;
 
        if (info->match & IP6T_OWNER_GID)
-               if ((filp->f_gid != info->gid) ^
+               if ((filp->f_cred->fsgid != info->gid) ^
                    !!(info->invert & IP6T_OWNER_GID))
                        return false;
 
@@ -93,14 +93,14 @@ owner_mt(const struct sk_buff *skb, const struct xt_match_param *par)
                       (XT_OWNER_UID | XT_OWNER_GID)) == 0;
 
        if (info->match & XT_OWNER_UID)
-               if ((filp->f_uid >= info->uid_min &&
-                   filp->f_uid <= info->uid_max) ^
+               if ((filp->f_cred->fsuid >= info->uid_min &&
+                   filp->f_cred->fsuid <= info->uid_max) ^
                    !(info->invert & XT_OWNER_UID))
                        return false;
 
        if (info->match & XT_OWNER_GID)
-               if ((filp->f_gid >= info->gid_min &&
-                   filp->f_gid <= info->gid_max) ^
+               if ((filp->f_cred->fsgid >= info->gid_min &&
+                   filp->f_cred->fsgid <= info->gid_max) ^
                    !(info->invert & XT_OWNER_GID))
                        return false;
 
index 9f1ea4a..e9c05b8 100644 (file)
@@ -609,7 +609,7 @@ static int nr_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
        } else {
                source = &addr->fsa_ax25.sax25_call;
 
-               user = ax25_findbyuid(current->euid);
+               user = ax25_findbyuid(current_euid());
                if (user) {
                        nr->user_addr   = user->call;
                        ax25_uid_put(user);
@@ -683,7 +683,7 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr,
                }
                source = (ax25_address *)dev->dev_addr;
 
-               user = ax25_findbyuid(current->euid);
+               user = ax25_findbyuid(current_euid());
                if (user) {
                        nr->user_addr   = user->call;
                        ax25_uid_put(user);
index 0c1cc76..0139264 100644 (file)
@@ -690,7 +690,7 @@ static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 
        source = &addr->srose_call;
 
-       user = ax25_findbyuid(current->euid);
+       user = ax25_findbyuid(current_euid());
        if (user) {
                rose->source_call = user->call;
                ax25_uid_put(user);
@@ -791,7 +791,7 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
                        goto out_release;
                }
 
-               user = ax25_findbyuid(current->euid);
+               user = ax25_findbyuid(current_euid());
                if (!user) {
                        err = -EINVAL;
                        goto out_release;
index 9a8ff68..ad8c7a7 100644 (file)
@@ -287,6 +287,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn,
                              time_t expiry,
                              u32 kvno)
 {
+       const struct cred *cred = current_cred();
        struct key *key;
        int ret;
 
@@ -297,7 +298,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn,
 
        _enter("");
 
-       key = key_alloc(&key_type_rxrpc, "x", 0, 0, current, 0,
+       key = key_alloc(&key_type_rxrpc, "x", 0, 0, cred, 0,
                        KEY_ALLOC_NOT_IN_QUOTA);
        if (IS_ERR(key)) {
                _leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key));
@@ -340,10 +341,11 @@ EXPORT_SYMBOL(rxrpc_get_server_data_key);
  */
 struct key *rxrpc_get_null_key(const char *keyname)
 {
+       const struct cred *cred = current_cred();
        struct key *key;
        int ret;
 
-       key = key_alloc(&key_type_rxrpc, keyname, 0, 0, current,
+       key = key_alloc(&key_type_rxrpc, keyname, 0, 0, cred,
                        KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA);
        if (IS_ERR(key))
                return key;
index 0ebaff6..0ef4e30 100644 (file)
@@ -260,14 +260,14 @@ static u32 flow_get_rtclassid(const struct sk_buff *skb)
 static u32 flow_get_skuid(const struct sk_buff *skb)
 {
        if (skb->sk && skb->sk->sk_socket && skb->sk->sk_socket->file)
-               return skb->sk->sk_socket->file->f_uid;
+               return skb->sk->sk_socket->file->f_cred->fsuid;
        return 0;
 }
 
 static u32 flow_get_skgid(const struct sk_buff *skb)
 {
        if (skb->sk && skb->sk->sk_socket && skb->sk->sk_socket->file)
-               return skb->sk->sk_socket->file->f_gid;
+               return skb->sk->sk_socket->file->f_cred->fsgid;
        return 0;
 }
 
index 76ba80a..072e2e5 100644 (file)
@@ -491,8 +491,8 @@ static struct socket *sock_alloc(void)
        sock = SOCKET_I(inode);
 
        inode->i_mode = S_IFSOCK | S_IRWXUGO;
-       inode->i_uid = current->fsuid;
-       inode->i_gid = current->fsgid;
+       inode->i_uid = current_fsuid();
+       inode->i_gid = current_fsgid();
 
        get_cpu_var(sockets_in_use)++;
        put_cpu_var(sockets_in_use);
index cb216b2..0443f83 100644 (file)
@@ -350,16 +350,18 @@ EXPORT_SYMBOL_GPL(rpcauth_lookup_credcache);
 struct rpc_cred *
 rpcauth_lookupcred(struct rpc_auth *auth, int flags)
 {
-       struct auth_cred acred = {
-               .uid = current->fsuid,
-               .gid = current->fsgid,
-               .group_info = current->group_info,
-       };
+       struct auth_cred acred;
        struct rpc_cred *ret;
+       const struct cred *cred = current_cred();
 
        dprintk("RPC:       looking up %s cred\n",
                auth->au_ops->au_name);
-       get_group_info(acred.group_info);
+
+       memset(&acred, 0, sizeof(acred));
+       acred.uid = cred->fsuid;
+       acred.gid = cred->fsgid;
+       acred.group_info = get_group_info(((struct cred *)cred)->group_info);
+
        ret = auth->au_ops->lookup_cred(auth, &acred, flags);
        put_group_info(acred.group_info);
        return ret;
index 66d5ac4..b152e2b 100644 (file)
@@ -467,8 +467,7 @@ static int unix_listen(struct socket *sock, int backlog)
        sk->sk_state            = TCP_LISTEN;
        /* set credentials so connect can copy them */
        sk->sk_peercred.pid     = task_tgid_vnr(current);
-       sk->sk_peercred.uid     = current->euid;
-       sk->sk_peercred.gid     = current->egid;
+       current_euid_egid(&sk->sk_peercred.uid, &sk->sk_peercred.gid);
        err = 0;
 
 out_unlock:
@@ -1126,8 +1125,7 @@ restart:
        newsk->sk_state         = TCP_ESTABLISHED;
        newsk->sk_type          = sk->sk_type;
        newsk->sk_peercred.pid  = task_tgid_vnr(current);
-       newsk->sk_peercred.uid  = current->euid;
-       newsk->sk_peercred.gid  = current->egid;
+       current_euid_egid(&newsk->sk_peercred.uid, &newsk->sk_peercred.gid);
        newu = unix_sk(newsk);
        newsk->sk_sleep         = &newu->peer_wait;
        otheru = unix_sk(other);
@@ -1187,8 +1185,9 @@ static int unix_socketpair(struct socket *socka, struct socket *sockb)
        unix_peer(ska)=skb;
        unix_peer(skb)=ska;
        ska->sk_peercred.pid = skb->sk_peercred.pid = task_tgid_vnr(current);
-       ska->sk_peercred.uid = skb->sk_peercred.uid = current->euid;
-       ska->sk_peercred.gid = skb->sk_peercred.gid = current->egid;
+       current_euid_egid(&skb->sk_peercred.uid, &skb->sk_peercred.gid);
+       ska->sk_peercred.uid = skb->sk_peercred.uid;
+       ska->sk_peercred.gid = skb->sk_peercred.gid;
 
        if (ska->sk_type != SOCK_DGRAM) {
                ska->sk_state = TCP_ESTABLISHED;
index 6b9fe3e..c67cec8 100755 (executable)
@@ -167,6 +167,17 @@ if ($arch eq "x86_64") {
     $objcopy .= " -O elf32-i386";
     $cc .= " -m32";
 
+} elsif ($arch eq "sh") {
+    $section_regex = "Disassembly of section\\s+(\\S+):";
+    $function_regex = "^([0-9a-fA-F]+)\\s+<(.*?)>:";
+    $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount\$";
+    $type = ".long";
+
+    # force flags for this arch
+    $ld .= " -m shlelf_linux";
+    $objcopy .= " -O elf32-sh-linux";
+    $cc .= " -m32";
+
 } else {
     die "Arch $arch is not supported with CONFIG_FTRACE_MCOUNT_RECORD";
 }
index 2458748..2dce66f 100644 (file)
@@ -32,24 +32,19 @@ static int cap_quota_on(struct dentry *dentry)
        return 0;
 }
 
-static int cap_bprm_alloc_security(struct linux_binprm *bprm)
+static int cap_bprm_check_security (struct linux_binprm *bprm)
 {
        return 0;
 }
 
-static void cap_bprm_free_security(struct linux_binprm *bprm)
+static void cap_bprm_committing_creds(struct linux_binprm *bprm)
 {
 }
 
-static void cap_bprm_post_apply_creds(struct linux_binprm *bprm)
+static void cap_bprm_committed_creds(struct linux_binprm *bprm)
 {
 }
 
-static int cap_bprm_check_security(struct linux_binprm *bprm)
-{
-       return 0;
-}
-
 static int cap_sb_alloc_security(struct super_block *sb)
 {
        return 0;
@@ -64,7 +59,7 @@ static int cap_sb_copy_data(char *orig, char *copy)
        return 0;
 }
 
-static int cap_sb_kern_mount(struct super_block *sb, void *data)
+static int cap_sb_kern_mount(struct super_block *sb, int flags, void *data)
 {
        return 0;
 }
@@ -330,7 +325,7 @@ static int cap_file_receive(struct file *file)
        return 0;
 }
 
-static int cap_dentry_open(struct file *file)
+static int cap_dentry_open(struct file *file, const struct cred *cred)
 {
        return 0;
 }
@@ -340,15 +335,29 @@ static int cap_task_create(unsigned long clone_flags)
        return 0;
 }
 
-static int cap_task_alloc_security(struct task_struct *p)
+static void cap_cred_free(struct cred *cred)
+{
+}
+
+static int cap_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp)
 {
        return 0;
 }
 
-static void cap_task_free_security(struct task_struct *p)
+static void cap_cred_commit(struct cred *new, const struct cred *old)
 {
 }
 
+static int cap_kernel_act_as(struct cred *new, u32 secid)
+{
+       return 0;
+}
+
+static int cap_kernel_create_files_as(struct cred *new, struct inode *inode)
+{
+       return 0;
+}
+
 static int cap_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
 {
        return 0;
@@ -750,7 +759,7 @@ static void cap_release_secctx(char *secdata, u32 seclen)
 }
 
 #ifdef CONFIG_KEYS
-static int cap_key_alloc(struct key *key, struct task_struct *ctx,
+static int cap_key_alloc(struct key *key, const struct cred *cred,
                         unsigned long flags)
 {
        return 0;
@@ -760,7 +769,7 @@ static void cap_key_free(struct key *key)
 {
 }
 
-static int cap_key_permission(key_ref_t key_ref, struct task_struct *context,
+static int cap_key_permission(key_ref_t key_ref, const struct cred *cred,
                              key_perm_t perm)
 {
        return 0;
@@ -814,8 +823,7 @@ void security_fixup_ops(struct security_operations *ops)
        set_to_cap_if_null(ops, ptrace_may_access);
        set_to_cap_if_null(ops, ptrace_traceme);
        set_to_cap_if_null(ops, capget);
-       set_to_cap_if_null(ops, capset_check);
-       set_to_cap_if_null(ops, capset_set);
+       set_to_cap_if_null(ops, capset);
        set_to_cap_if_null(ops, acct);
        set_to_cap_if_null(ops, capable);
        set_to_cap_if_null(ops, quotactl);
@@ -824,11 +832,9 @@ void security_fixup_ops(struct security_operations *ops)
        set_to_cap_if_null(ops, syslog);
        set_to_cap_if_null(ops, settime);
        set_to_cap_if_null(ops, vm_enough_memory);
-       set_to_cap_if_null(ops, bprm_alloc_security);
-       set_to_cap_if_null(ops, bprm_free_security);
-       set_to_cap_if_null(ops, bprm_apply_creds);
-       set_to_cap_if_null(ops, bprm_post_apply_creds);
-       set_to_cap_if_null(ops, bprm_set_security);
+       set_to_cap_if_null(ops, bprm_set_creds);
+       set_to_cap_if_null(ops, bprm_committing_creds);
+       set_to_cap_if_null(ops, bprm_committed_creds);
        set_to_cap_if_null(ops, bprm_check_security);
        set_to_cap_if_null(ops, bprm_secureexec);
        set_to_cap_if_null(ops, sb_alloc_security);
@@ -890,10 +896,13 @@ void security_fixup_ops(struct security_operations *ops)
        set_to_cap_if_null(ops, file_receive);
        set_to_cap_if_null(ops, dentry_open);
        set_to_cap_if_null(ops, task_create);
-       set_to_cap_if_null(ops, task_alloc_security);
-       set_to_cap_if_null(ops, task_free_security);
+       set_to_cap_if_null(ops, cred_free);
+       set_to_cap_if_null(ops, cred_prepare);
+       set_to_cap_if_null(ops, cred_commit);
+       set_to_cap_if_null(ops, kernel_act_as);
+       set_to_cap_if_null(ops, kernel_create_files_as);
        set_to_cap_if_null(ops, task_setuid);
-       set_to_cap_if_null(ops, task_post_setuid);
+       set_to_cap_if_null(ops, task_fix_setuid);
        set_to_cap_if_null(ops, task_setgid);
        set_to_cap_if_null(ops, task_setpgid);
        set_to_cap_if_null(ops, task_getpgid);
@@ -910,7 +919,6 @@ void security_fixup_ops(struct security_operations *ops)
        set_to_cap_if_null(ops, task_wait);
        set_to_cap_if_null(ops, task_kill);
        set_to_cap_if_null(ops, task_prctl);
-       set_to_cap_if_null(ops, task_reparent_to_init);
        set_to_cap_if_null(ops, task_to_inode);
        set_to_cap_if_null(ops, ipc_permission);
        set_to_cap_if_null(ops, ipc_getsecid);
index 3976613..7971354 100644 (file)
@@ -8,6 +8,7 @@
  */
 
 #include <linux/capability.h>
+#include <linux/audit.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -29,7 +30,7 @@
 
 int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
 {
-       NETLINK_CB(skb).eff_cap = current->cap_effective;
+       NETLINK_CB(skb).eff_cap = current_cap();
        return 0;
 }
 
@@ -39,23 +40,41 @@ int cap_netlink_recv(struct sk_buff *skb, int cap)
                return -EPERM;
        return 0;
 }
-
 EXPORT_SYMBOL(cap_netlink_recv);
 
-/*
+/**
+ * cap_capable - Determine whether a task has a particular effective capability
+ * @tsk: The task to query
+ * @cap: The capability to check for
+ * @audit: Whether to write an audit message or not
+ *
+ * Determine whether the nominated task has the specified capability amongst
+ * its effective set, returning 0 if it does, -ve if it does not.
+ *
  * NOTE WELL: cap_capable() cannot be used like the kernel's capable()
- * function.  That is, it has the reverse semantics: cap_capable()
- * returns 0 when a task has a capability, but the kernel's capable()
- * returns 1 for this case.
+ * function.  That is, it has the reverse semantics: cap_capable() returns 0
+ * when a task has a capability, but the kernel's capable() returns 1 for this
+ * case.
  */
-int cap_capable (struct task_struct *tsk, int cap)
+int cap_capable(struct task_struct *tsk, int cap, int audit)
 {
+       __u32 cap_raised;
+
        /* Derived from include/linux/sched.h:capable. */
-       if (cap_raised(tsk->cap_effective, cap))
-               return 0;
-       return -EPERM;
+       rcu_read_lock();
+       cap_raised = cap_raised(__task_cred(tsk)->cap_effective, cap);
+       rcu_read_unlock();
+       return cap_raised ? 0 : -EPERM;
 }
 
+/**
+ * cap_settime - Determine whether the current process may set the system clock
+ * @ts: The time to set
+ * @tz: The timezone to set
+ *
+ * Determine whether the current process may set the system clock and timezone
+ * information, returning 0 if permission granted, -ve if denied.
+ */
 int cap_settime(struct timespec *ts, struct timezone *tz)
 {
        if (!capable(CAP_SYS_TIME))
@@ -63,121 +82,157 @@ int cap_settime(struct timespec *ts, struct timezone *tz)
        return 0;
 }
 
+/**
+ * cap_ptrace_may_access - Determine whether the current process may access
+ *                        another
+ * @child: The process to be accessed
+ * @mode: The mode of attachment.
+ *
+ * Determine whether a process may access another, returning 0 if permission
+ * granted, -ve if denied.
+ */
 int cap_ptrace_may_access(struct task_struct *child, unsigned int mode)
 {
-       /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */
-       if (cap_issubset(child->cap_permitted, current->cap_permitted))
-               return 0;
-       if (capable(CAP_SYS_PTRACE))
-               return 0;
-       return -EPERM;
+       int ret = 0;
+
+       rcu_read_lock();
+       if (!cap_issubset(__task_cred(child)->cap_permitted,
+                         current_cred()->cap_permitted) &&
+           !capable(CAP_SYS_PTRACE))
+               ret = -EPERM;
+       rcu_read_unlock();
+       return ret;
 }
 
+/**
+ * cap_ptrace_traceme - Determine whether another process may trace the current
+ * @parent: The task proposed to be the tracer
+ *
+ * Determine whether the nominated task is permitted to trace the current
+ * process, returning 0 if permission is granted, -ve if denied.
+ */
 int cap_ptrace_traceme(struct task_struct *parent)
 {
-       /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */
-       if (cap_issubset(current->cap_permitted, parent->cap_permitted))
-               return 0;
-       if (has_capability(parent, CAP_SYS_PTRACE))
-               return 0;
-       return -EPERM;
+       int ret = 0;
+
+       rcu_read_lock();
+       if (!cap_issubset(current_cred()->cap_permitted,
+                         __task_cred(parent)->cap_permitted) &&
+           !has_capability(parent, CAP_SYS_PTRACE))
+               ret = -EPERM;
+       rcu_read_unlock();
+       return ret;
 }
 
-int cap_capget (struct task_struct *target, kernel_cap_t *effective,
-               kernel_cap_t *inheritable, kernel_cap_t *permitted)
+/**
+ * cap_capget - Retrieve a task's capability sets
+ * @target: The task from which to retrieve the capability sets
+ * @effective: The place to record the effective set
+ * @inheritable: The place to record the inheritable set
+ * @permitted: The place to record the permitted set
+ *
+ * This function retrieves the capabilities of the nominated task and returns
+ * them to the caller.
+ */
+int cap_capget(struct task_struct *target, kernel_cap_t *effective,
+              kernel_cap_t *inheritable, kernel_cap_t *permitted)
 {
+       const struct cred *cred;
+
        /* Derived from kernel/capability.c:sys_capget. */
-       *effective = target->cap_effective;
-       *inheritable = target->cap_inheritable;
-       *permitted = target->cap_permitted;
+       rcu_read_lock();
+       cred = __task_cred(target);
+       *effective   = cred->cap_effective;
+       *inheritable = cred->cap_inheritable;
+       *permitted   = cred->cap_permitted;
+       rcu_read_unlock();
        return 0;
 }
 
-#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
-
-static inline int cap_block_setpcap(struct task_struct *target)
-{
-       /*
-        * No support for remote process capability manipulation with
-        * filesystem capability support.
-        */
-       return (target != current);
-}
-
+/*
+ * Determine whether the inheritable capabilities are limited to the old
+ * permitted set.  Returns 1 if they are limited, 0 if they are not.
+ */
 static inline int cap_inh_is_capped(void)
 {
-       /*
-        * Return 1 if changes to the inheritable set are limited
-        * to the old permitted set. That is, if the current task
-        * does *not* possess the CAP_SETPCAP capability.
-        */
-       return (cap_capable(current, CAP_SETPCAP) != 0);
-}
-
-static inline int cap_limit_ptraced_target(void) { return 1; }
-
-#else /* ie., ndef CONFIG_SECURITY_FILE_CAPABILITIES */
+#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
 
-static inline int cap_block_setpcap(struct task_struct *t) { return 0; }
-static inline int cap_inh_is_capped(void) { return 1; }
-static inline int cap_limit_ptraced_target(void)
-{
-       return !capable(CAP_SETPCAP);
+       /* they are so limited unless the current task has the CAP_SETPCAP
+        * capability
+        */
+       if (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0)
+               return 0;
+#endif
+       return 1;
 }
 
-#endif /* def CONFIG_SECURITY_FILE_CAPABILITIES */
-
-int cap_capset_check (struct task_struct *target, kernel_cap_t *effective,
-                     kernel_cap_t *inheritable, kernel_cap_t *permitted)
-{
-       if (cap_block_setpcap(target)) {
-               return -EPERM;
-       }
-       if (cap_inh_is_capped()
-           && !cap_issubset(*inheritable,
-                            cap_combine(target->cap_inheritable,
-                                        current->cap_permitted))) {
+/**
+ * cap_capset - Validate and apply proposed changes to current's capabilities
+ * @new: The proposed new credentials; alterations should be made here
+ * @old: The current task's current credentials
+ * @effective: A pointer to the proposed new effective capabilities set
+ * @inheritable: A pointer to the proposed new inheritable capabilities set
+ * @permitted: A pointer to the proposed new permitted capabilities set
+ *
+ * This function validates and applies a proposed mass change to the current
+ * process's capability sets.  The changes are made to the proposed new
+ * credentials, and assuming no error, will be committed by the caller of LSM.
+ */
+int cap_capset(struct cred *new,
+              const struct cred *old,
+              const kernel_cap_t *effective,
+              const kernel_cap_t *inheritable,
+              const kernel_cap_t *permitted)
+{
+       if (cap_inh_is_capped() &&
+           !cap_issubset(*inheritable,
+                         cap_combine(old->cap_inheritable,
+                                     old->cap_permitted)))
                /* incapable of using this inheritable set */
                return -EPERM;
-       }
+
        if (!cap_issubset(*inheritable,
-                          cap_combine(target->cap_inheritable,
-                                      current->cap_bset))) {
+                         cap_combine(old->cap_inheritable,
+                                     old->cap_bset)))
                /* no new pI capabilities outside bounding set */
                return -EPERM;
-       }
 
        /* verify restrictions on target's new Permitted set */
-       if (!cap_issubset (*permitted,
-                          cap_combine (target->cap_permitted,
-                                       current->cap_permitted))) {
+       if (!cap_issubset(*permitted, old->cap_permitted))
                return -EPERM;
-       }
 
        /* verify the _new_Effective_ is a subset of the _new_Permitted_ */
-       if (!cap_issubset (*effective, *permitted)) {
+       if (!cap_issubset(*effective, *permitted))
                return -EPERM;
-       }
 
+       new->cap_effective   = *effective;
+       new->cap_inheritable = *inheritable;
+       new->cap_permitted   = *permitted;
        return 0;
 }
 
-void cap_capset_set (struct task_struct *target, kernel_cap_t *effective,
-                    kernel_cap_t *inheritable, kernel_cap_t *permitted)
-{
-       target->cap_effective = *effective;
-       target->cap_inheritable = *inheritable;
-       target->cap_permitted = *permitted;
-}
-
+/*
+ * Clear proposed capability sets for execve().
+ */
 static inline void bprm_clear_caps(struct linux_binprm *bprm)
 {
-       cap_clear(bprm->cap_post_exec_permitted);
+       cap_clear(bprm->cred->cap_permitted);
        bprm->cap_effective = false;
 }
 
 #ifdef CONFIG_SECURITY_FILE_CAPABILITIES
 
+/**
+ * cap_inode_need_killpriv - Determine if inode change affects privileges
+ * @dentry: The inode/dentry in being changed with change marked ATTR_KILL_PRIV
+ *
+ * Determine if an inode having a change applied that's marked ATTR_KILL_PRIV
+ * affects the security markings on that inode, and if it is, should
+ * inode_killpriv() be invoked or the change rejected?
+ *
+ * Returns 0 if granted; +ve if granted, but inode_killpriv() is required; and
+ * -ve to deny the change.
+ */
 int cap_inode_need_killpriv(struct dentry *dentry)
 {
        struct inode *inode = dentry->d_inode;
@@ -192,6 +247,14 @@ int cap_inode_need_killpriv(struct dentry *dentry)
        return 1;
 }
 
+/**
+ * cap_inode_killpriv - Erase the security markings on an inode
+ * @dentry: The inode/dentry to alter
+ *
+ * Erase the privilege-enhancing security markings on an inode.
+ *
+ * Returns 0 if successful, -ve on error.
+ */
 int cap_inode_killpriv(struct dentry *dentry)
 {
        struct inode *inode = dentry->d_inode;
@@ -202,19 +265,75 @@ int cap_inode_killpriv(struct dentry *dentry)
        return inode->i_op->removexattr(dentry, XATTR_NAME_CAPS);
 }
 
-static inline int cap_from_disk(struct vfs_cap_data *caps,
-                               struct linux_binprm *bprm, unsigned size)
+/*
+ * Calculate the new process capability sets from the capability sets attached
+ * to a file.
+ */
+static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
+                                         struct linux_binprm *bprm,
+                                         bool *effective)
+{
+       struct cred *new = bprm->cred;
+       unsigned i;
+       int ret = 0;
+
+       if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
+               *effective = true;
+
+       CAP_FOR_EACH_U32(i) {
+               __u32 permitted = caps->permitted.cap[i];
+               __u32 inheritable = caps->inheritable.cap[i];
+
+               /*
+                * pP' = (X & fP) | (pI & fI)
+                */
+               new->cap_permitted.cap[i] =
+                       (new->cap_bset.cap[i] & permitted) |
+                       (new->cap_inheritable.cap[i] & inheritable);
+
+               if (permitted & ~new->cap_permitted.cap[i])
+                       /* insufficient to execute correctly */
+                       ret = -EPERM;
+       }
+
+       /*
+        * For legacy apps, with no internal support for recognizing they
+        * do not have enough capabilities, we return an error if they are
+        * missing some "forced" (aka file-permitted) capabilities.
+        */
+       return *effective ? ret : 0;
+}
+
+/*
+ * Extract the on-exec-apply capability sets for an executable file.
+ */
+int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps)
 {
+       struct inode *inode = dentry->d_inode;
        __u32 magic_etc;
        unsigned tocopy, i;
-       int ret;
+       int size;
+       struct vfs_cap_data caps;
+
+       memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data));
+
+       if (!inode || !inode->i_op || !inode->i_op->getxattr)
+               return -ENODATA;
+
+       size = inode->i_op->getxattr((struct dentry *)dentry, XATTR_NAME_CAPS, &caps,
+                                  XATTR_CAPS_SZ);
+       if (size == -ENODATA || size == -EOPNOTSUPP)
+               /* no data, that's ok */
+               return -ENODATA;
+       if (size < 0)
+               return size;
 
        if (size < sizeof(magic_etc))
                return -EINVAL;
 
-       magic_etc = le32_to_cpu(caps->magic_etc);
+       cpu_caps->magic_etc = magic_etc = le32_to_cpu(caps.magic_etc);
 
-       switch ((magic_etc & VFS_CAP_REVISION_MASK)) {
+       switch (magic_etc & VFS_CAP_REVISION_MASK) {
        case VFS_CAP_REVISION_1:
                if (size != XATTR_CAPS_SZ_1)
                        return -EINVAL;
@@ -229,77 +348,48 @@ static inline int cap_from_disk(struct vfs_cap_data *caps,
                return -EINVAL;
        }
 
-       if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE) {
-               bprm->cap_effective = true;
-       } else {
-               bprm->cap_effective = false;
-       }
-
-       ret = 0;
-
        CAP_FOR_EACH_U32(i) {
-               __u32 value_cpu;
-
-               if (i >= tocopy) {
-                       /*
-                        * Legacy capability sets have no upper bits
-                        */
-                       bprm->cap_post_exec_permitted.cap[i] = 0;
-                       continue;
-               }
-               /*
-                * pP' = (X & fP) | (pI & fI)
-                */
-               value_cpu = le32_to_cpu(caps->data[i].permitted);
-               bprm->cap_post_exec_permitted.cap[i] =
-                       (current->cap_bset.cap[i] & value_cpu) |
-                       (current->cap_inheritable.cap[i] &
-                               le32_to_cpu(caps->data[i].inheritable));
-               if (value_cpu & ~bprm->cap_post_exec_permitted.cap[i]) {
-                       /*
-                        * insufficient to execute correctly
-                        */
-                       ret = -EPERM;
-               }
+               if (i >= tocopy)
+                       break;
+               cpu_caps->permitted.cap[i] = le32_to_cpu(caps.data[i].permitted);
+               cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable);
        }
 
-       /*
-        * For legacy apps, with no internal support for recognizing they
-        * do not have enough capabilities, we return an error if they are
-        * missing some "forced" (aka file-permitted) capabilities.
-        */
-       return bprm->cap_effective ? ret : 0;
+       return 0;
 }
 
-/* Locate any VFS capabilities: */
-static int get_file_caps(struct linux_binprm *bprm)
+/*
+ * Attempt to get the on-exec apply capability sets for an executable file from
+ * its xattrs and, if present, apply them to the proposed credentials being
+ * constructed by execve().
+ */
+static int get_file_caps(struct linux_binprm *bprm, bool *effective)
 {
        struct dentry *dentry;
        int rc = 0;
-       struct vfs_cap_data vcaps;
-       struct inode *inode;
+       struct cpu_vfs_cap_data vcaps;
 
        bprm_clear_caps(bprm);
 
+       if (!file_caps_enabled)
+               return 0;
+
        if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID)
                return 0;
 
        dentry = dget(bprm->file->f_dentry);
-       inode = dentry->d_inode;
-       if (!inode->i_op || !inode->i_op->getxattr)
-               goto out;
 
-       rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, &vcaps,
-                                  XATTR_CAPS_SZ);
-       if (rc == -ENODATA || rc == -EOPNOTSUPP) {
-               /* no data, that's ok */
-               rc = 0;
+       rc = get_vfs_caps_from_disk(dentry, &vcaps);
+       if (rc < 0) {
+               if (rc == -EINVAL)
+                       printk(KERN_NOTICE "%s: get_vfs_caps_from_disk returned %d for %s\n",
+                               __func__, rc, bprm->filename);
+               else if (rc == -ENODATA)
+                       rc = 0;
                goto out;
        }
-       if (rc < 0)
-               goto out;
 
-       rc = cap_from_disk(&vcaps, bprm, rc);
+       rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective);
        if (rc == -EINVAL)
                printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
                       __func__, rc, bprm->filename);
@@ -323,18 +413,57 @@ int cap_inode_killpriv(struct dentry *dentry)
        return 0;
 }
 
-static inline int get_file_caps(struct linux_binprm *bprm)
+int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps)
+{
+       memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data));
+       return -ENODATA;
+}
+
+static inline int get_file_caps(struct linux_binprm *bprm, bool *effective)
 {
        bprm_clear_caps(bprm);
        return 0;
 }
 #endif
 
-int cap_bprm_set_security (struct linux_binprm *bprm)
+/*
+ * Determine whether a exec'ing process's new permitted capabilities should be
+ * limited to just what it already has.
+ *
+ * This prevents processes that are being ptraced from gaining access to
+ * CAP_SETPCAP, unless the process they're tracing already has it, and the
+ * binary they're executing has filecaps that elevate it.
+ *
+ *  Returns 1 if they should be limited, 0 if they are not.
+ */
+static inline int cap_limit_ptraced_target(void)
+{
+#ifndef CONFIG_SECURITY_FILE_CAPABILITIES
+       if (capable(CAP_SETPCAP))
+               return 0;
+#endif
+       return 1;
+}
+
+/**
+ * cap_bprm_set_creds - Set up the proposed credentials for execve().
+ * @bprm: The execution parameters, including the proposed creds
+ *
+ * Set up the proposed credentials for a new execution context being
+ * constructed by execve().  The proposed creds in @bprm->cred is altered,
+ * which won't take effect immediately.  Returns 0 if successful, -ve on error.
+ */
+int cap_bprm_set_creds(struct linux_binprm *bprm)
 {
+       const struct cred *old = current_cred();
+       struct cred *new = bprm->cred;
+       bool effective;
        int ret;
 
-       ret = get_file_caps(bprm);
+       effective = false;
+       ret = get_file_caps(bprm, &effective);
+       if (ret < 0)
+               return ret;
 
        if (!issecure(SECURE_NOROOT)) {
                /*
@@ -342,75 +471,113 @@ int cap_bprm_set_security (struct linux_binprm *bprm)
                 * executables under compatibility mode, we override the
                 * capability sets for the file.
                 *
-                * If only the real uid is 0, we do not set the effective
-                * bit.
+                * If only the real uid is 0, we do not set the effective bit.
                 */
-               if (bprm->e_uid == 0 || current->uid == 0) {
+               if (new->euid == 0 || new->uid == 0) {
                        /* pP' = (cap_bset & ~0) | (pI & ~0) */
-                       bprm->cap_post_exec_permitted = cap_combine(
-                               current->cap_bset, current->cap_inheritable
-                               );
-                       bprm->cap_effective = (bprm->e_uid == 0);
-                       ret = 0;
+                       new->cap_permitted = cap_combine(old->cap_bset,
+                                                        old->cap_inheritable);
                }
+               if (new->euid == 0)
+                       effective = true;
        }
 
-       return ret;
-}
-
-void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
-{
-       if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
-           !cap_issubset(bprm->cap_post_exec_permitted,
-                         current->cap_permitted)) {
-               set_dumpable(current->mm, suid_dumpable);
-               current->pdeath_signal = 0;
-
-               if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
-                       if (!capable(CAP_SETUID)) {
-                               bprm->e_uid = current->uid;
-                               bprm->e_gid = current->gid;
-                       }
-                       if (cap_limit_ptraced_target()) {
-                               bprm->cap_post_exec_permitted = cap_intersect(
-                                       bprm->cap_post_exec_permitted,
-                                       current->cap_permitted);
-                       }
+       /* Don't let someone trace a set[ug]id/setpcap binary with the revised
+        * credentials unless they have the appropriate permit
+        */
+       if ((new->euid != old->uid ||
+            new->egid != old->gid ||
+            !cap_issubset(new->cap_permitted, old->cap_permitted)) &&
+           bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
+               /* downgrade; they get no more than they had, and maybe less */
+               if (!capable(CAP_SETUID)) {
+                       new->euid = new->uid;
+                       new->egid = new->gid;
                }
+               if (cap_limit_ptraced_target())
+                       new->cap_permitted = cap_intersect(new->cap_permitted,
+                                                          old->cap_permitted);
        }
 
-       current->suid = current->euid = current->fsuid = bprm->e_uid;
-       current->sgid = current->egid = current->fsgid = bprm->e_gid;
+       new->suid = new->fsuid = new->euid;
+       new->sgid = new->fsgid = new->egid;
 
-       /* For init, we want to retain the capabilities set
-        * in the init_task struct. Thus we skip the usual
-        * capability rules */
+       /* For init, we want to retain the capabilities set in the initial
+        * task.  Thus we skip the usual capability rules
+        */
        if (!is_global_init(current)) {
-               current->cap_permitted = bprm->cap_post_exec_permitted;
-               if (bprm->cap_effective)
-                       current->cap_effective = bprm->cap_post_exec_permitted;
+               if (effective)
+                       new->cap_effective = new->cap_permitted;
                else
-                       cap_clear(current->cap_effective);
+                       cap_clear(new->cap_effective);
        }
+       bprm->cap_effective = effective;
 
-       /* AUD: Audit candidate if current->cap_effective is set */
+       /*
+        * Audit candidate if current->cap_effective is set
+        *
+        * We do not bother to audit if 3 things are true:
+        *   1) cap_effective has all caps
+        *   2) we are root
+        *   3) root is supposed to have all caps (SECURE_NOROOT)
+        * Since this is just a normal root execing a process.
+        *
+        * Number 1 above might fail if you don't have a full bset, but I think
+        * that is interesting information to audit.
+        */
+       if (!cap_isclear(new->cap_effective)) {
+               if (!cap_issubset(CAP_FULL_SET, new->cap_effective) ||
+                   new->euid != 0 || new->uid != 0 ||
+                   issecure(SECURE_NOROOT)) {
+                       ret = audit_log_bprm_fcaps(bprm, new, old);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
 
-       current->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
+       new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
+       return 0;
 }
 
-int cap_bprm_secureexec (struct linux_binprm *bprm)
+/**
+ * cap_bprm_secureexec - Determine whether a secure execution is required
+ * @bprm: The execution parameters
+ *
+ * Determine whether a secure execution is required, return 1 if it is, and 0
+ * if it is not.
+ *
+ * The credentials have been committed by this point, and so are no longer
+ * available through @bprm->cred.
+ */
+int cap_bprm_secureexec(struct linux_binprm *bprm)
 {
-       if (current->uid != 0) {
+       const struct cred *cred = current_cred();
+
+       if (cred->uid != 0) {
                if (bprm->cap_effective)
                        return 1;
-               if (!cap_isclear(bprm->cap_post_exec_permitted))
+               if (!cap_isclear(cred->cap_permitted))
                        return 1;
        }
 
-       return (current->euid != current->uid ||
-               current->egid != current->gid);
+       return (cred->euid != cred->uid ||
+               cred->egid != cred->gid);
 }
 
+/**
+ * cap_inode_setxattr - Determine whether an xattr may be altered
+ * @dentry: The inode/dentry being altered
+ * @name: The name of the xattr to be changed
+ * @value: The value that the xattr will be changed to
+ * @size: The size of value
+ * @flags: The replacement flag
+ *
+ * Determine whether an xattr may be altered or set on an inode, returning 0 if
+ * permission is granted, -ve if denied.
+ *
+ * This is used to make sure security xattrs don't get updated or set by those
+ * who aren't privileged to do so.
+ */
 int cap_inode_setxattr(struct dentry *dentry, const char *name,
                       const void *value, size_t size, int flags)
 {
@@ -418,28 +585,42 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name,
                if (!capable(CAP_SETFCAP))
                        return -EPERM;
                return 0;
-       } else if (!strncmp(name, XATTR_SECURITY_PREFIX,
+       }
+
+       if (!strncmp(name, XATTR_SECURITY_PREFIX,
                     sizeof(XATTR_SECURITY_PREFIX) - 1)  &&
            !capable(CAP_SYS_ADMIN))
                return -EPERM;
        return 0;
 }
 
+/**
+ * cap_inode_removexattr - Determine whether an xattr may be removed
+ * @dentry: The inode/dentry being altered
+ * @name: The name of the xattr to be changed
+ *
+ * Determine whether an xattr may be removed from an inode, returning 0 if
+ * permission is granted, -ve if denied.
+ *
+ * This is used to make sure security xattrs don't get removed by those who
+ * aren't privileged to remove them.
+ */
 int cap_inode_removexattr(struct dentry *dentry, const char *name)
 {
        if (!strcmp(name, XATTR_NAME_CAPS)) {
                if (!capable(CAP_SETFCAP))
                        return -EPERM;
                return 0;
-       } else if (!strncmp(name, XATTR_SECURITY_PREFIX,
+       }
+
+       if (!strncmp(name, XATTR_SECURITY_PREFIX,
                     sizeof(XATTR_SECURITY_PREFIX) - 1)  &&
            !capable(CAP_SYS_ADMIN))
                return -EPERM;
        return 0;
 }
 
-/* moved from kernel/sys.c. */
-/* 
+/*
  * cap_emulate_setxuid() fixes the effective / permitted capabilities of
  * a process after a call to setuid, setreuid, or setresuid.
  *
@@ -453,10 +634,10 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name)
  *  3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective
  *  capabilities are set to the permitted capabilities.
  *
- *  fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should 
+ *  fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should
  *  never happen.
  *
- *  -astor 
+ *  -astor
  *
  * cevans - New behaviour, Oct '99
  * A process may, via prctl(), elect to keep its capabilities when it
@@ -468,61 +649,60 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name)
  * files..
  * Thanks to Olaf Kirch and Peter Benie for spotting this.
  */
-static inline void cap_emulate_setxuid (int old_ruid, int old_euid,
-                                       int old_suid)
+static inline void cap_emulate_setxuid(struct cred *new, const struct cred *old)
 {
-       if ((old_ruid == 0 || old_euid == 0 || old_suid == 0) &&
-           (current->uid != 0 && current->euid != 0 && current->suid != 0) &&
+       if ((old->uid == 0 || old->euid == 0 || old->suid == 0) &&
+           (new->uid != 0 && new->euid != 0 && new->suid != 0) &&
            !issecure(SECURE_KEEP_CAPS)) {
-               cap_clear (current->cap_permitted);
-               cap_clear (current->cap_effective);
-       }
-       if (old_euid == 0 && current->euid != 0) {
-               cap_clear (current->cap_effective);
-       }
-       if (old_euid != 0 && current->euid == 0) {
-               current->cap_effective = current->cap_permitted;
+               cap_clear(new->cap_permitted);
+               cap_clear(new->cap_effective);
        }
+       if (old->euid == 0 && new->euid != 0)
+               cap_clear(new->cap_effective);
+       if (old->euid != 0 && new->euid == 0)
+               new->cap_effective = new->cap_permitted;
 }
 
-int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid,
-                         int flags)
+/**
+ * cap_task_fix_setuid - Fix up the results of setuid() call
+ * @new: The proposed credentials
+ * @old: The current task's current credentials
+ * @flags: Indications of what has changed
+ *
+ * Fix up the results of setuid() call before the credential changes are
+ * actually applied, returning 0 to grant the changes, -ve to deny them.
+ */
+int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags)
 {
        switch (flags) {
        case LSM_SETID_RE:
        case LSM_SETID_ID:
        case LSM_SETID_RES:
-               /* Copied from kernel/sys.c:setreuid/setuid/setresuid. */
-               if (!issecure (SECURE_NO_SETUID_FIXUP)) {
-                       cap_emulate_setxuid (old_ruid, old_euid, old_suid);
-               }
+               /* juggle the capabilities to follow [RES]UID changes unless
+                * otherwise suppressed */
+               if (!issecure(SECURE_NO_SETUID_FIXUP))
+                       cap_emulate_setxuid(new, old);
                break;
-       case LSM_SETID_FS:
-               {
-                       uid_t old_fsuid = old_ruid;
 
-                       /* Copied from kernel/sys.c:setfsuid. */
-
-                       /*
-                        * FIXME - is fsuser used for all CAP_FS_MASK capabilities?
-                        *          if not, we might be a bit too harsh here.
-                        */
-
-                       if (!issecure (SECURE_NO_SETUID_FIXUP)) {
-                               if (old_fsuid == 0 && current->fsuid != 0) {
-                                       current->cap_effective =
-                                               cap_drop_fs_set(
-                                                   current->cap_effective);
-                               }
-                               if (old_fsuid != 0 && current->fsuid == 0) {
-                                       current->cap_effective =
-                                               cap_raise_fs_set(
-                                                   current->cap_effective,
-                                                   current->cap_permitted);
-                               }
-                       }
-                       break;
+       case LSM_SETID_FS:
+               /* juggle the capabilties to follow FSUID changes, unless
+                * otherwise suppressed
+                *
+                * FIXME - is fsuser used for all CAP_FS_MASK capabilities?
+                *          if not, we might be a bit too harsh here.
+                */
+               if (!issecure(SECURE_NO_SETUID_FIXUP)) {
+                       if (old->fsuid == 0 && new->fsuid != 0)
+                               new->cap_effective =
+                                       cap_drop_fs_set(new->cap_effective);
+
+                       if (old->fsuid != 0 && new->fsuid == 0)
+                               new->cap_effective =
+                                       cap_raise_fs_set(new->cap_effective,
+                                                        new->cap_permitted);
                }
+               break;
+
        default:
                return -EINVAL;
        }
@@ -543,42 +723,71 @@ int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid,
  */
 static int cap_safe_nice(struct task_struct *p)
 {
-       if (!cap_issubset(p->cap_permitted, current->cap_permitted) &&
-           !capable(CAP_SYS_NICE))
+       int is_subset;
+
+       rcu_read_lock();
+       is_subset = cap_issubset(__task_cred(p)->cap_permitted,
+                                current_cred()->cap_permitted);
+       rcu_read_unlock();
+
+       if (!is_subset && !capable(CAP_SYS_NICE))
                return -EPERM;
        return 0;
 }
 
-int cap_task_setscheduler (struct task_struct *p, int policy,
+/**
+ * cap_task_setscheduler - Detemine if scheduler policy change is permitted
+ * @p: The task to affect
+ * @policy: The policy to effect
+ * @lp: The parameters to the scheduling policy
+ *
+ * Detemine if the requested scheduler policy change is permitted for the
+ * specified task, returning 0 if permission is granted, -ve if denied.
+ */
+int cap_task_setscheduler(struct task_struct *p, int policy,
                           struct sched_param *lp)
 {
        return cap_safe_nice(p);
 }
 
-int cap_task_setioprio (struct task_struct *p, int ioprio)
+/**
+ * cap_task_ioprio - Detemine if I/O priority change is permitted
+ * @p: The task to affect
+ * @ioprio: The I/O priority to set
+ *
+ * Detemine if the requested I/O priority change is permitted for the specified
+ * task, returning 0 if permission is granted, -ve if denied.
+ */
+int cap_task_setioprio(struct task_struct *p, int ioprio)
 {
        return cap_safe_nice(p);
 }
 
-int cap_task_setnice (struct task_struct *p, int nice)
+/**
+ * cap_task_ioprio - Detemine if task priority change is permitted
+ * @p: The task to affect
+ * @nice: The nice value to set
+ *
+ * Detemine if the requested task priority change is permitted for the
+ * specified task, returning 0 if permission is granted, -ve if denied.
+ */
+int cap_task_setnice(struct task_struct *p, int nice)
 {
        return cap_safe_nice(p);
 }
 
 /*
- * called from kernel/sys.c for prctl(PR_CABSET_DROP)
- * done without task_capability_lock() because it introduces
- * no new races - i.e. only another task doing capget() on
- * this task could get inconsistent info.  There can be no
- * racing writer bc a task can only change its own caps.
+ * Implement PR_CAPBSET_DROP.  Attempt to remove the specified capability from
+ * the current task's bounding set.  Returns 0 on success, -ve on error.
  */
-static long cap_prctl_drop(unsigned long cap)
+static long cap_prctl_drop(struct cred *new, unsigned long cap)
 {
        if (!capable(CAP_SETPCAP))
                return -EPERM;
        if (!cap_valid(cap))
                return -EINVAL;
-       cap_lower(current->cap_bset, cap);
+
+       cap_lower(new->cap_bset, cap);
        return 0;
 }
 
@@ -598,22 +807,42 @@ int cap_task_setnice (struct task_struct *p, int nice)
 }
 #endif
 
+/**
+ * cap_task_prctl - Implement process control functions for this security module
+ * @option: The process control function requested
+ * @arg2, @arg3, @arg4, @arg5: The argument data for this function
+ *
+ * Allow process control functions (sys_prctl()) to alter capabilities; may
+ * also deny access to other functions not otherwise implemented here.
+ *
+ * Returns 0 or +ve on success, -ENOSYS if this function is not implemented
+ * here, other -ve on error.  If -ENOSYS is returned, sys_prctl() and other LSM
+ * modules will consider performing the function.
+ */
 int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
-                  unsigned long arg4, unsigned long arg5, long *rc_p)
+                  unsigned long arg4, unsigned long arg5)
 {
+       struct cred *new;
        long error = 0;
 
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+
        switch (option) {
        case PR_CAPBSET_READ:
+               error = -EINVAL;
                if (!cap_valid(arg2))
-                       error = -EINVAL;
-               else
-                       error = !!cap_raised(current->cap_bset, arg2);
-               break;
+                       goto error;
+               error = !!cap_raised(new->cap_bset, arg2);
+               goto no_change;
+
 #ifdef CONFIG_SECURITY_FILE_CAPABILITIES
        case PR_CAPBSET_DROP:
-               error = cap_prctl_drop(arg2);
-               break;
+               error = cap_prctl_drop(new, arg2);
+               if (error < 0)
+                       goto error;
+               goto changed;
 
        /*
         * The next four prctl's remain to assist with transitioning a
@@ -635,12 +864,12 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
         * capability-based-privilege environment.
         */
        case PR_SET_SECUREBITS:
-               if ((((current->securebits & SECURE_ALL_LOCKS) >> 1)
-                    & (current->securebits ^ arg2))                  /*[1]*/
-                   || ((current->securebits & SECURE_ALL_LOCKS
-                        & ~arg2))                                    /*[2]*/
-                   || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/
-                   || (cap_capable(current, CAP_SETPCAP) != 0)) {    /*[4]*/
+               error = -EPERM;
+               if ((((new->securebits & SECURE_ALL_LOCKS) >> 1)
+                    & (new->securebits ^ arg2))                        /*[1]*/
+                   || ((new->securebits & SECURE_ALL_LOCKS & ~arg2))   /*[2]*/
+                   || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS))   /*[3]*/
+                   || (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0) /*[4]*/
                        /*
                         * [1] no changing of bits that are locked
                         * [2] no unlocking of locks
@@ -648,65 +877,80 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
                         * [4] doing anything requires privilege (go read about
                         *     the "sendmail capabilities bug")
                         */
-                       error = -EPERM;  /* cannot change a locked bit */
-               } else {
-                       current->securebits = arg2;
-               }
-               break;
+                   )
+                       /* cannot change a locked bit */
+                       goto error;
+               new->securebits = arg2;
+               goto changed;
+
        case PR_GET_SECUREBITS:
-               error = current->securebits;
-               break;
+               error = new->securebits;
+               goto no_change;
 
 #endif /* def CONFIG_SECURITY_FILE_CAPABILITIES */
 
        case PR_GET_KEEPCAPS:
                if (issecure(SECURE_KEEP_CAPS))
                        error = 1;
-               break;
+               goto no_change;
+
        case PR_SET_KEEPCAPS:
+               error = -EINVAL;
                if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */
-                       error = -EINVAL;
-               else if (issecure(SECURE_KEEP_CAPS_LOCKED))
-                       error = -EPERM;
-               else if (arg2)
-                       current->securebits |= issecure_mask(SECURE_KEEP_CAPS);
+                       goto error;
+               error = -EPERM;
+               if (issecure(SECURE_KEEP_CAPS_LOCKED))
+                       goto error;
+               if (arg2)
+                       new->securebits |= issecure_mask(SECURE_KEEP_CAPS);
                else
-                       current->securebits &=
-                               ~issecure_mask(SECURE_KEEP_CAPS);
-               break;
+                       new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
+               goto changed;
 
        default:
                /* No functionality available - continue with default */
-               return 0;
+               error = -ENOSYS;
+               goto error;
        }
 
        /* Functionality provided */
-       *rc_p = error;
-       return 1;
-}
+changed:
+       return commit_creds(new);
 
-void cap_task_reparent_to_init (struct task_struct *p)
-{
-       cap_set_init_eff(p->cap_effective);
-       cap_clear(p->cap_inheritable);
-       cap_set_full(p->cap_permitted);
-       p->securebits = SECUREBITS_DEFAULT;
-       return;
+no_change:
+       error = 0;
+error:
+       abort_creds(new);
+       return error;
 }
 
-int cap_syslog (int type)
+/**
+ * cap_syslog - Determine whether syslog function is permitted
+ * @type: Function requested
+ *
+ * Determine whether the current process is permitted to use a particular
+ * syslog function, returning 0 if permission is granted, -ve if not.
+ */
+int cap_syslog(int type)
 {
        if ((type != 3 && type != 10) && !capable(CAP_SYS_ADMIN))
                return -EPERM;
        return 0;
 }
 
+/**
+ * cap_vm_enough_memory - Determine whether a new virtual mapping is permitted
+ * @mm: The VM space in which the new mapping is to be made
+ * @pages: The size of the mapping
+ *
+ * Determine whether the allocation of a new virtual mapping by the current
+ * task is permitted, returning 0 if permission is granted, -ve if not.
+ */
 int cap_vm_enough_memory(struct mm_struct *mm, long pages)
 {
        int cap_sys_admin = 0;
 
-       if (cap_capable(current, CAP_SYS_ADMIN) == 0)
+       if (cap_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT) == 0)
                cap_sys_admin = 1;
        return __vm_enough_memory(mm, pages, cap_sys_admin);
 }
-
index 239098f..81932ab 100644 (file)
@@ -12,8 +12,8 @@
 #ifndef _INTERNAL_H
 #define _INTERNAL_H
 
+#include <linux/sched.h>
 #include <linux/key-type.h>
-#include <linux/key-ui.h>
 
 static inline __attribute__((format(printf, 1, 2)))
 void no_printk(const char *fmt, ...)
@@ -26,7 +26,7 @@ void no_printk(const char *fmt, ...)
 #define kleave(FMT, ...) \
        printk(KERN_DEBUG "<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
 #define kdebug(FMT, ...) \
-       printk(KERN_DEBUG "xxx" FMT"yyy\n", ##__VA_ARGS__)
+       printk(KERN_DEBUG "   "FMT"\n", ##__VA_ARGS__)
 #else
 #define kenter(FMT, ...) \
        no_printk(KERN_DEBUG "==> %s("FMT")\n", __func__, ##__VA_ARGS__)
@@ -82,6 +82,9 @@ extern struct mutex key_construction_mutex;
 extern wait_queue_head_t request_key_conswq;
 
 
+extern struct key_type *key_type_lookup(const char *type);
+extern void key_type_put(struct key_type *ktype);
+
 extern int __key_link(struct key *keyring, struct key *key);
 
 extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
@@ -95,7 +98,7 @@ extern struct key *keyring_search_instkey(struct key *keyring,
 typedef int (*key_match_func_t)(const struct key *, const void *);
 
 extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
-                                   struct task_struct *tsk,
+                                   const struct cred *cred,
                                    struct key_type *type,
                                    const void *description,
                                    key_match_func_t match);
@@ -103,13 +106,13 @@ extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
 extern key_ref_t search_process_keyrings(struct key_type *type,
                                         const void *description,
                                         key_match_func_t match,
-                                        struct task_struct *tsk);
+                                        const struct cred *cred);
 
 extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
 
-extern int install_user_keyrings(struct task_struct *tsk);
-extern int install_thread_keyring(struct task_struct *tsk);
-extern int install_process_keyring(struct task_struct *tsk);
+extern int install_user_keyrings(void);
+extern int install_thread_keyring_to_cred(struct cred *);
+extern int install_process_keyring_to_cred(struct cred *);
 
 extern struct key *request_key_and_link(struct key_type *type,
                                        const char *description,
@@ -119,12 +122,39 @@ extern struct key *request_key_and_link(struct key_type *type,
                                        struct key *dest_keyring,
                                        unsigned long flags);
 
+extern key_ref_t lookup_user_key(key_serial_t id, int create, int partial,
+                                key_perm_t perm);
+
+extern long join_session_keyring(const char *name);
+
+/*
+ * check to see whether permission is granted to use a key in the desired way
+ */
+extern int key_task_permission(const key_ref_t key_ref,
+                              const struct cred *cred,
+                              key_perm_t perm);
+
+static inline int key_permission(const key_ref_t key_ref, key_perm_t perm)
+{
+       return key_task_permission(key_ref, current_cred(), perm);
+}
+
+/* required permissions */
+#define        KEY_VIEW        0x01    /* require permission to view attributes */
+#define        KEY_READ        0x02    /* require permission to read content */
+#define        KEY_WRITE       0x04    /* require permission to update / modify */
+#define        KEY_SEARCH      0x08    /* require permission to search (keyring) or find (key) */
+#define        KEY_LINK        0x10    /* require permission to link */
+#define        KEY_SETATTR     0x20    /* require permission to change attributes */
+#define        KEY_ALL         0x3f    /* all the above permissions */
+
 /*
  * request_key authorisation
  */
 struct request_key_auth {
        struct key              *target_key;
-       struct task_struct      *context;
+       struct key              *dest_keyring;
+       const struct cred       *cred;
        void                    *callout_info;
        size_t                  callout_len;
        pid_t                   pid;
@@ -133,7 +163,8 @@ struct request_key_auth {
 extern struct key_type key_type_request_key_auth;
 extern struct key *request_key_auth_new(struct key *target,
                                        const void *callout_info,
-                                       size_t callout_len);
+                                       size_t callout_len,
+                                       struct key *dest_keyring);
 
 extern struct key *key_get_instantiation_authkey(key_serial_t target_id);
 
index 14948cf..f76c8a5 100644 (file)
@@ -218,7 +218,7 @@ serial_exists:
  *   instantiate the key or discard it before returning
  */
 struct key *key_alloc(struct key_type *type, const char *desc,
-                     uid_t uid, gid_t gid, struct task_struct *ctx,
+                     uid_t uid, gid_t gid, const struct cred *cred,
                      key_perm_t perm, unsigned long flags)
 {
        struct key_user *user = NULL;
@@ -294,7 +294,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
 #endif
 
        /* let the security module know about the key */
-       ret = security_key_alloc(key, ctx, flags);
+       ret = security_key_alloc(key, cred, flags);
        if (ret < 0)
                goto security_error;
 
@@ -391,7 +391,7 @@ static int __key_instantiate_and_link(struct key *key,
                                      const void *data,
                                      size_t datalen,
                                      struct key *keyring,
-                                     struct key *instkey)
+                                     struct key *authkey)
 {
        int ret, awaken;
 
@@ -421,8 +421,8 @@ static int __key_instantiate_and_link(struct key *key,
                                ret = __key_link(keyring, key);
 
                        /* disable the authorisation key */
-                       if (instkey)
-                               key_revoke(instkey);
+                       if (authkey)
+                               key_revoke(authkey);
                }
        }
 
@@ -444,14 +444,14 @@ int key_instantiate_and_link(struct key *key,
                             const void *data,
                             size_t datalen,
                             struct key *keyring,
-                            struct key *instkey)
+                            struct key *authkey)
 {
        int ret;
 
        if (keyring)
                down_write(&keyring->sem);
 
-       ret = __key_instantiate_and_link(key, data, datalen, keyring, instkey);
+       ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey);
 
        if (keyring)
                up_write(&keyring->sem);
@@ -469,7 +469,7 @@ EXPORT_SYMBOL(key_instantiate_and_link);
 int key_negate_and_link(struct key *key,
                        unsigned timeout,
                        struct key *keyring,
-                       struct key *instkey)
+                       struct key *authkey)
 {
        struct timespec now;
        int ret, awaken;
@@ -504,8 +504,8 @@ int key_negate_and_link(struct key *key,
                        ret = __key_link(keyring, key);
 
                /* disable the authorisation key */
-               if (instkey)
-                       key_revoke(instkey);
+               if (authkey)
+                       key_revoke(authkey);
        }
 
        mutex_unlock(&key_construction_mutex);
@@ -743,6 +743,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
                               key_perm_t perm,
                               unsigned long flags)
 {
+       const struct cred *cred = current_cred();
        struct key_type *ktype;
        struct key *keyring, *key = NULL;
        key_ref_t key_ref;
@@ -802,8 +803,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
        }
 
        /* allocate a new key */
-       key = key_alloc(ktype, description, current->fsuid, current->fsgid,
-                       current, perm, flags);
+       key = key_alloc(ktype, description, cred->fsuid, cred->fsgid, cred,
+                       perm, flags);
        if (IS_ERR(key)) {
                key_ref = ERR_CAST(key);
                goto error_3;
index acc9c89..7c72baa 100644 (file)
@@ -103,7 +103,7 @@ asmlinkage long sys_add_key(const char __user *_type,
        }
 
        /* find the target keyring (which must be writable) */
-       keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
+       keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
        if (IS_ERR(keyring_ref)) {
                ret = PTR_ERR(keyring_ref);
                goto error3;
@@ -185,7 +185,7 @@ asmlinkage long sys_request_key(const char __user *_type,
        /* get the destination keyring if specified */
        dest_ref = NULL;
        if (destringid) {
-               dest_ref = lookup_user_key(NULL, destringid, 1, 0, KEY_WRITE);
+               dest_ref = lookup_user_key(destringid, 1, 0, KEY_WRITE);
                if (IS_ERR(dest_ref)) {
                        ret = PTR_ERR(dest_ref);
                        goto error3;
@@ -235,7 +235,7 @@ long keyctl_get_keyring_ID(key_serial_t id, int create)
        key_ref_t key_ref;
        long ret;
 
-       key_ref = lookup_user_key(NULL, id, create, 0, KEY_SEARCH);
+       key_ref = lookup_user_key(id, create, 0, KEY_SEARCH);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error;
@@ -308,7 +308,7 @@ long keyctl_update_key(key_serial_t id,
        }
 
        /* find the target key (which must be writable) */
-       key_ref = lookup_user_key(NULL, id, 0, 0, KEY_WRITE);
+       key_ref = lookup_user_key(id, 0, 0, KEY_WRITE);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error2;
@@ -336,7 +336,7 @@ long keyctl_revoke_key(key_serial_t id)
        key_ref_t key_ref;
        long ret;
 
-       key_ref = lookup_user_key(NULL, id, 0, 0, KEY_WRITE);
+       key_ref = lookup_user_key(id, 0, 0, KEY_WRITE);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error;
@@ -362,7 +362,7 @@ long keyctl_keyring_clear(key_serial_t ringid)
        key_ref_t keyring_ref;
        long ret;
 
-       keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
+       keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
        if (IS_ERR(keyring_ref)) {
                ret = PTR_ERR(keyring_ref);
                goto error;
@@ -388,13 +388,13 @@ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)
        key_ref_t keyring_ref, key_ref;
        long ret;
 
-       keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
+       keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
        if (IS_ERR(keyring_ref)) {
                ret = PTR_ERR(keyring_ref);
                goto error;
        }
 
-       key_ref = lookup_user_key(NULL, id, 1, 0, KEY_LINK);
+       key_ref = lookup_user_key(id, 1, 0, KEY_LINK);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error2;
@@ -422,13 +422,13 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)
        key_ref_t keyring_ref, key_ref;
        long ret;
 
-       keyring_ref = lookup_user_key(NULL, ringid, 0, 0, KEY_WRITE);
+       keyring_ref = lookup_user_key(ringid, 0, 0, KEY_WRITE);
        if (IS_ERR(keyring_ref)) {
                ret = PTR_ERR(keyring_ref);
                goto error;
        }
 
-       key_ref = lookup_user_key(NULL, id, 0, 0, 0);
+       key_ref = lookup_user_key(id, 0, 0, 0);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error2;
@@ -464,7 +464,7 @@ long keyctl_describe_key(key_serial_t keyid,
        char *tmpbuf;
        long ret;
 
-       key_ref = lookup_user_key(NULL, keyid, 0, 1, KEY_VIEW);
+       key_ref = lookup_user_key(keyid, 0, 1, KEY_VIEW);
        if (IS_ERR(key_ref)) {
                /* viewing a key under construction is permitted if we have the
                 * authorisation token handy */
@@ -472,7 +472,7 @@ long keyctl_describe_key(key_serial_t keyid,
                        instkey = key_get_instantiation_authkey(keyid);
                        if (!IS_ERR(instkey)) {
                                key_put(instkey);
-                               key_ref = lookup_user_key(NULL, keyid,
+                               key_ref = lookup_user_key(keyid,
                                                          0, 1, 0);
                                if (!IS_ERR(key_ref))
                                        goto okay;
@@ -557,7 +557,7 @@ long keyctl_keyring_search(key_serial_t ringid,
        }
 
        /* get the keyring at which to begin the search */
-       keyring_ref = lookup_user_key(NULL, ringid, 0, 0, KEY_SEARCH);
+       keyring_ref = lookup_user_key(ringid, 0, 0, KEY_SEARCH);
        if (IS_ERR(keyring_ref)) {
                ret = PTR_ERR(keyring_ref);
                goto error2;
@@ -566,7 +566,7 @@ long keyctl_keyring_search(key_serial_t ringid,
        /* get the destination keyring if specified */
        dest_ref = NULL;
        if (destringid) {
-               dest_ref = lookup_user_key(NULL, destringid, 1, 0, KEY_WRITE);
+               dest_ref = lookup_user_key(destringid, 1, 0, KEY_WRITE);
                if (IS_ERR(dest_ref)) {
                        ret = PTR_ERR(dest_ref);
                        goto error3;
@@ -636,7 +636,7 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
        long ret;
 
        /* find the key first */
-       key_ref = lookup_user_key(NULL, keyid, 0, 0, 0);
+       key_ref = lookup_user_key(keyid, 0, 0, 0);
        if (IS_ERR(key_ref)) {
                ret = -ENOKEY;
                goto error;
@@ -699,7 +699,7 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
        if (uid == (uid_t) -1 && gid == (gid_t) -1)
                goto error;
 
-       key_ref = lookup_user_key(NULL, id, 1, 1, KEY_SETATTR);
+       key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error;
@@ -804,7 +804,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
        if (perm & ~(KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL))
                goto error;
 
-       key_ref = lookup_user_key(NULL, id, 1, 1, KEY_SETATTR);
+       key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error;
@@ -817,7 +817,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
        down_write(&key->sem);
 
        /* if we're not the sysadmin, we can only change a key that we own */
-       if (capable(CAP_SYS_ADMIN) || key->uid == current->fsuid) {
+       if (capable(CAP_SYS_ADMIN) || key->uid == current_fsuid()) {
                key->perm = perm;
                ret = 0;
        }
@@ -829,6 +829,60 @@ error:
 
 } /* end keyctl_setperm_key() */
 
+/*
+ * get the destination keyring for instantiation
+ */
+static long get_instantiation_keyring(key_serial_t ringid,
+                                     struct request_key_auth *rka,
+                                     struct key **_dest_keyring)
+{
+       key_ref_t dkref;
+
+       /* just return a NULL pointer if we weren't asked to make a link */
+       if (ringid == 0) {
+               *_dest_keyring = NULL;
+               return 0;
+       }
+
+       /* if a specific keyring is nominated by ID, then use that */
+       if (ringid > 0) {
+               dkref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+               if (IS_ERR(dkref))
+                       return PTR_ERR(dkref);
+               *_dest_keyring = key_ref_to_ptr(dkref);
+               return 0;
+       }
+
+       if (ringid == KEY_SPEC_REQKEY_AUTH_KEY)
+               return -EINVAL;
+
+       /* otherwise specify the destination keyring recorded in the
+        * authorisation key (any KEY_SPEC_*_KEYRING) */
+       if (ringid >= KEY_SPEC_REQUESTOR_KEYRING) {
+               *_dest_keyring = rka->dest_keyring;
+               return 0;
+       }
+
+       return -ENOKEY;
+}
+
+/*
+ * change the request_key authorisation key on the current process
+ */
+static int keyctl_change_reqkey_auth(struct key *key)
+{
+       struct cred *new;
+
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+
+       key_put(new->request_key_auth);
+       new->request_key_auth = key_get(key);
+
+       return commit_creds(new);
+}
+
 /*****************************************************************************/
 /*
  * instantiate the key with the specified payload, and, if one is given, link
@@ -839,13 +893,15 @@ long keyctl_instantiate_key(key_serial_t id,
                            size_t plen,
                            key_serial_t ringid)
 {
+       const struct cred *cred = current_cred();
        struct request_key_auth *rka;
-       struct key *instkey;
-       key_ref_t keyring_ref;
+       struct key *instkey, *dest_keyring;
        void *payload;
        long ret;
        bool vm = false;
 
+       kenter("%d,,%zu,%d", id, plen, ringid);
+
        ret = -EINVAL;
        if (plen > 1024 * 1024 - 1)
                goto error;
@@ -853,7 +909,7 @@ long keyctl_instantiate_key(key_serial_t id,
        /* the appropriate instantiation authorisation key must have been
         * assumed before calling this */
        ret = -EPERM;
-       instkey = current->request_key_auth;
+       instkey = cred->request_key_auth;
        if (!instkey)
                goto error;
 
@@ -883,28 +939,20 @@ long keyctl_instantiate_key(key_serial_t id,
 
        /* find the destination keyring amongst those belonging to the
         * requesting task */
-       keyring_ref = NULL;
-       if (ringid) {
-               keyring_ref = lookup_user_key(rka->context, ringid, 1, 0,
-                                             KEY_WRITE);
-               if (IS_ERR(keyring_ref)) {
-                       ret = PTR_ERR(keyring_ref);
-                       goto error2;
-               }
-       }
+       ret = get_instantiation_keyring(ringid, rka, &dest_keyring);
+       if (ret < 0)
+               goto error2;
 
        /* instantiate the key and link it into a keyring */
        ret = key_instantiate_and_link(rka->target_key, payload, plen,
-                                      key_ref_to_ptr(keyring_ref), instkey);
+                                      dest_keyring, instkey);
 
-       key_ref_put(keyring_ref);
+       key_put(dest_keyring);
 
        /* discard the assumed authority if it's just been disabled by
         * instantiation of the key */
-       if (ret == 0) {
-               key_put(current->request_key_auth);
-               current->request_key_auth = NULL;
-       }
+       if (ret == 0)
+               keyctl_change_reqkey_auth(NULL);
 
 error2:
        if (!vm)
@@ -923,15 +971,17 @@ error:
  */
 long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
 {
+       const struct cred *cred = current_cred();
        struct request_key_auth *rka;
-       struct key *instkey;
-       key_ref_t keyring_ref;
+       struct key *instkey, *dest_keyring;
        long ret;
 
+       kenter("%d,%u,%d", id, timeout, ringid);
+
        /* the appropriate instantiation authorisation key must have been
         * assumed before calling this */
        ret = -EPERM;
-       instkey = current->request_key_auth;
+       instkey = cred->request_key_auth;
        if (!instkey)
                goto error;
 
@@ -941,27 +991,20 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
 
        /* find the destination keyring if present (which must also be
         * writable) */
-       keyring_ref = NULL;
-       if (ringid) {
-               keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
-               if (IS_ERR(keyring_ref)) {
-                       ret = PTR_ERR(keyring_ref);
-                       goto error;
-               }
-       }
+       ret = get_instantiation_keyring(ringid, rka, &dest_keyring);
+       if (ret < 0)
+               goto error;
 
        /* instantiate the key and link it into a keyring */
        ret = key_negate_and_link(rka->target_key, timeout,
-                                 key_ref_to_ptr(keyring_ref), instkey);
+                                 dest_keyring, instkey);
 
-       key_ref_put(keyring_ref);
+       key_put(dest_keyring);
 
        /* discard the assumed authority if it's just been disabled by
         * instantiation of the key */
-       if (ret == 0) {
-               key_put(current->request_key_auth);
-               current->request_key_auth = NULL;
-       }
+       if (ret == 0)
+               keyctl_change_reqkey_auth(NULL);
 
 error:
        return ret;
@@ -975,35 +1018,56 @@ error:
  */
 long keyctl_set_reqkey_keyring(int reqkey_defl)
 {
-       int ret;
+       struct cred *new;
+       int ret, old_setting;
+
+       old_setting = current_cred_xxx(jit_keyring);
+
+       if (reqkey_defl == KEY_REQKEY_DEFL_NO_CHANGE)
+               return old_setting;
+
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
 
        switch (reqkey_defl) {
        case KEY_REQKEY_DEFL_THREAD_KEYRING:
-               ret = install_thread_keyring(current);
+               ret = install_thread_keyring_to_cred(new);
                if (ret < 0)
-                       return ret;
+                       goto error;
                goto set;
 
        case KEY_REQKEY_DEFL_PROCESS_KEYRING:
-               ret = install_process_keyring(current);
-               if (ret < 0)
-                       return ret;
+               ret = install_process_keyring_to_cred(new);
+               if (ret < 0) {
+                       if (ret != -EEXIST)
+                               goto error;
+                       ret = 0;
+               }
+               goto set;
 
        case KEY_REQKEY_DEFL_DEFAULT:
        case KEY_REQKEY_DEFL_SESSION_KEYRING:
        case KEY_REQKEY_DEFL_USER_KEYRING:
        case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
-       set:
-               current->jit_keyring = reqkey_defl;
+       case KEY_REQKEY_DEFL_REQUESTOR_KEYRING:
+               goto set;
 
        case KEY_REQKEY_DEFL_NO_CHANGE:
-               return current->jit_keyring;
-
        case KEY_REQKEY_DEFL_GROUP_KEYRING:
        default:
-               return -EINVAL;
+               ret = -EINVAL;
+               goto error;
        }
 
+set:
+       new->jit_keyring = reqkey_defl;
+       commit_creds(new);
+       return old_setting;
+error:
+       abort_creds(new);
+       return -EINVAL;
+
 } /* end keyctl_set_reqkey_keyring() */
 
 /*****************************************************************************/
@@ -1018,7 +1082,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
        time_t expiry;
        long ret;
 
-       key_ref = lookup_user_key(NULL, id, 1, 1, KEY_SETATTR);
+       key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error;
@@ -1062,9 +1126,7 @@ long keyctl_assume_authority(key_serial_t id)
 
        /* we divest ourselves of authority if given an ID of 0 */
        if (id == 0) {
-               key_put(current->request_key_auth);
-               current->request_key_auth = NULL;
-               ret = 0;
+               ret = keyctl_change_reqkey_auth(NULL);
                goto error;
        }
 
@@ -1079,10 +1141,12 @@ long keyctl_assume_authority(key_serial_t id)
                goto error;
        }
 
-       key_put(current->request_key_auth);
-       current->request_key_auth = authkey;
-       ret = authkey->serial;
+       ret = keyctl_change_reqkey_auth(authkey);
+       if (ret < 0)
+               goto error;
+       key_put(authkey);
 
+       ret = authkey->serial;
 error:
        return ret;
 
@@ -1105,7 +1169,7 @@ long keyctl_get_security(key_serial_t keyid,
        char *context;
        long ret;
 
-       key_ref = lookup_user_key(NULL, keyid, 0, 1, KEY_VIEW);
+       key_ref = lookup_user_key(keyid, 0, 1, KEY_VIEW);
        if (IS_ERR(key_ref)) {
                if (PTR_ERR(key_ref) != -EACCES)
                        return PTR_ERR(key_ref);
@@ -1117,7 +1181,7 @@ long keyctl_get_security(key_serial_t keyid,
                        return PTR_ERR(key_ref);
                key_put(instkey);
 
-               key_ref = lookup_user_key(NULL, keyid, 0, 1, 0);
+               key_ref = lookup_user_key(keyid, 0, 1, 0);
                if (IS_ERR(key_ref))
                        return PTR_ERR(key_ref);
        }
index a9ab8af..ed85157 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/security.h>
 #include <linux/seq_file.h>
 #include <linux/err.h>
+#include <keys/keyring-type.h>
 #include <asm/uaccess.h>
 #include "internal.h"
 
@@ -244,14 +245,14 @@ static long keyring_read(const struct key *keyring,
  * allocate a keyring and link into the destination keyring
  */
 struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
-                         struct task_struct *ctx, unsigned long flags,
+                         const struct cred *cred, unsigned long flags,
                          struct key *dest)
 {
        struct key *keyring;
        int ret;
 
        keyring = key_alloc(&key_type_keyring, description,
-                           uid, gid, ctx,
+                           uid, gid, cred,
                            (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
                            flags);
 
@@ -280,7 +281,7 @@ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
  * - we propagate the possession attribute from the keyring ref to the key ref
  */
 key_ref_t keyring_search_aux(key_ref_t keyring_ref,
-                            struct task_struct *context,
+                            const struct cred *cred,
                             struct key_type *type,
                             const void *description,
                             key_match_func_t match)
@@ -303,7 +304,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
        key_check(keyring);
 
        /* top keyring must have search permission to begin the search */
-        err = key_task_permission(keyring_ref, context, KEY_SEARCH);
+        err = key_task_permission(keyring_ref, cred, KEY_SEARCH);
        if (err < 0) {
                key_ref = ERR_PTR(err);
                goto error;
@@ -376,7 +377,7 @@ descend:
 
                /* key must have search permissions */
                if (key_task_permission(make_key_ref(key, possessed),
-                                       context, KEY_SEARCH) < 0)
+                                       cred, KEY_SEARCH) < 0)
                        continue;
 
                /* we set a different error code if we pass a negative key */
@@ -403,7 +404,7 @@ ascend:
                        continue;
 
                if (key_task_permission(make_key_ref(key, possessed),
-                                       context, KEY_SEARCH) < 0)
+                                       cred, KEY_SEARCH) < 0)
                        continue;
 
                /* stack the current position */
@@ -458,7 +459,7 @@ key_ref_t keyring_search(key_ref_t keyring,
        if (!type->match)
                return ERR_PTR(-ENOKEY);
 
-       return keyring_search_aux(keyring, current,
+       return keyring_search_aux(keyring, current->cred,
                                  type, description, type->match);
 
 } /* end keyring_search() */
index 3b41f9b..5d9fc7b 100644 (file)
 #include "internal.h"
 
 /*****************************************************************************/
-/*
- * check to see whether permission is granted to use a key in the desired way,
- * but permit the security modules to override
+/**
+ * key_task_permission - Check a key can be used
+ * @key_ref: The key to check
+ * @cred: The credentials to use
+ * @perm: The permissions to check for
+ *
+ * Check to see whether permission is granted to use a key in the desired way,
+ * but permit the security modules to override.
+ *
+ * The caller must hold either a ref on cred or must hold the RCU readlock or a
+ * spinlock.
  */
-int key_task_permission(const key_ref_t key_ref,
-                       struct task_struct *context,
+int key_task_permission(const key_ref_t key_ref, const struct cred *cred,
                        key_perm_t perm)
 {
        struct key *key;
@@ -29,7 +36,7 @@ int key_task_permission(const key_ref_t key_ref,
        key = key_ref_to_ptr(key_ref);
 
        /* use the second 8-bits of permissions for keys the caller owns */
-       if (key->uid == context->fsuid) {
+       if (key->uid == cred->fsuid) {
                kperm = key->perm >> 16;
                goto use_these_perms;
        }
@@ -37,15 +44,12 @@ int key_task_permission(const key_ref_t key_ref,
        /* use the third 8-bits of permissions for keys the caller has a group
         * membership in common with */
        if (key->gid != -1 && key->perm & KEY_GRP_ALL) {
-               if (key->gid == context->fsgid) {
+               if (key->gid == cred->fsgid) {
                        kperm = key->perm >> 8;
                        goto use_these_perms;
                }
 
-               task_lock(context);
-               ret = groups_search(context->group_info, key->gid);
-               task_unlock(context);
-
+               ret = groups_search(cred->group_info, key->gid);
                if (ret) {
                        kperm = key->perm >> 8;
                        goto use_these_perms;
@@ -56,6 +60,7 @@ int key_task_permission(const key_ref_t key_ref,
        kperm = key->perm;
 
 use_these_perms:
+
        /* use the top 8-bits of permissions for keys the caller possesses
         * - possessor permissions are additive with other permissions
         */
@@ -68,7 +73,7 @@ use_these_perms:
                return -EACCES;
 
        /* let LSM be the final arbiter */
-       return security_key_permission(key_ref, context, perm);
+       return security_key_permission(key_ref, cred, perm);
 
 } /* end key_task_permission() */
 
index f619170..7f508de 100644 (file)
@@ -136,8 +136,12 @@ static int proc_keys_show(struct seq_file *m, void *v)
        int rc;
 
        /* check whether the current task is allowed to view the key (assuming
-        * non-possession) */
-       rc = key_task_permission(make_key_ref(key, 0), current, KEY_VIEW);
+        * non-possession)
+        * - the caller holds a spinlock, and thus the RCU read lock, making our
+        *   access to __current_cred() safe
+        */
+       rc = key_task_permission(make_key_ref(key, 0), current_cred(),
+                                KEY_VIEW);
        if (rc < 0)
                return 0;
 
index 45b240a..2f5d89e 100644 (file)
@@ -40,13 +40,17 @@ struct key_user root_key_user = {
 /*
  * install user and user session keyrings for a particular UID
  */
-int install_user_keyrings(struct task_struct *tsk)
+int install_user_keyrings(void)
 {
-       struct user_struct *user = tsk->user;
+       struct user_struct *user;
+       const struct cred *cred;
        struct key *uid_keyring, *session_keyring;
        char buf[20];
        int ret;
 
+       cred = current_cred();
+       user = cred->user;
+
        kenter("%p{%u}", user, user->uid);
 
        if (user->uid_keyring) {
@@ -67,7 +71,7 @@ int install_user_keyrings(struct task_struct *tsk)
                uid_keyring = find_keyring_by_name(buf, true);
                if (IS_ERR(uid_keyring)) {
                        uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1,
-                                                   tsk, KEY_ALLOC_IN_QUOTA,
+                                                   cred, KEY_ALLOC_IN_QUOTA,
                                                    NULL);
                        if (IS_ERR(uid_keyring)) {
                                ret = PTR_ERR(uid_keyring);
@@ -83,7 +87,7 @@ int install_user_keyrings(struct task_struct *tsk)
                if (IS_ERR(session_keyring)) {
                        session_keyring =
                                keyring_alloc(buf, user->uid, (gid_t) -1,
-                                             tsk, KEY_ALLOC_IN_QUOTA, NULL);
+                                             cred, KEY_ALLOC_IN_QUOTA, NULL);
                        if (IS_ERR(session_keyring)) {
                                ret = PTR_ERR(session_keyring);
                                goto error_release;
@@ -115,140 +119,128 @@ error:
        return ret;
 }
 
-/*****************************************************************************/
 /*
- * deal with the UID changing
+ * install a fresh thread keyring directly to new credentials
  */
-void switch_uid_keyring(struct user_struct *new_user)
+int install_thread_keyring_to_cred(struct cred *new)
 {
-#if 0 /* do nothing for now */
-       struct key *old;
-
-       /* switch to the new user's session keyring if we were running under
-        * root's default session keyring */
-       if (new_user->uid != 0 &&
-           current->session_keyring == &root_session_keyring
-           ) {
-               atomic_inc(&new_user->session_keyring->usage);
-
-               task_lock(current);
-               old = current->session_keyring;
-               current->session_keyring = new_user->session_keyring;
-               task_unlock(current);
+       struct key *keyring;
 
-               key_put(old);
-       }
-#endif
+       keyring = keyring_alloc("_tid", new->uid, new->gid, new,
+                               KEY_ALLOC_QUOTA_OVERRUN, NULL);
+       if (IS_ERR(keyring))
+               return PTR_ERR(keyring);
 
-} /* end switch_uid_keyring() */
+       new->thread_keyring = keyring;
+       return 0;
+}
 
-/*****************************************************************************/
 /*
  * install a fresh thread keyring, discarding the old one
  */
-int install_thread_keyring(struct task_struct *tsk)
+static int install_thread_keyring(void)
 {
-       struct key *keyring, *old;
-       char buf[20];
+       struct cred *new;
        int ret;
 
-       sprintf(buf, "_tid.%u", tsk->pid);
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
 
-       keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
-                               KEY_ALLOC_QUOTA_OVERRUN, NULL);
-       if (IS_ERR(keyring)) {
-               ret = PTR_ERR(keyring);
-               goto error;
+       BUG_ON(new->thread_keyring);
+
+       ret = install_thread_keyring_to_cred(new);
+       if (ret < 0) {
+               abort_creds(new);
+               return ret;
        }
 
-       task_lock(tsk);
-       old = tsk->thread_keyring;
-       tsk->thread_keyring = keyring;
-       task_unlock(tsk);
+       return commit_creds(new);
+}
 
-       ret = 0;
+/*
+ * install a process keyring directly to a credentials struct
+ * - returns -EEXIST if there was already a process keyring, 0 if one installed,
+ *   and other -ve on any other error
+ */
+int install_process_keyring_to_cred(struct cred *new)
+{
+       struct key *keyring;
+       int ret;
 
-       key_put(old);
-error:
+       if (new->tgcred->process_keyring)
+               return -EEXIST;
+
+       keyring = keyring_alloc("_pid", new->uid, new->gid,
+                               new, KEY_ALLOC_QUOTA_OVERRUN, NULL);
+       if (IS_ERR(keyring))
+               return PTR_ERR(keyring);
+
+       spin_lock_irq(&new->tgcred->lock);
+       if (!new->tgcred->process_keyring) {
+               new->tgcred->process_keyring = keyring;
+               keyring = NULL;
+               ret = 0;
+       } else {
+               ret = -EEXIST;
+       }
+       spin_unlock_irq(&new->tgcred->lock);
+       key_put(keyring);
        return ret;
+}
 
-} /* end install_thread_keyring() */
-
-/*****************************************************************************/
 /*
  * make sure a process keyring is installed
+ * - we
  */
-int install_process_keyring(struct task_struct *tsk)
+static int install_process_keyring(void)
 {
-       struct key *keyring;
-       char buf[20];
+       struct cred *new;
        int ret;
 
-       might_sleep();
-
-       if (!tsk->signal->process_keyring) {
-               sprintf(buf, "_pid.%u", tsk->tgid);
-
-               keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
-                                       KEY_ALLOC_QUOTA_OVERRUN, NULL);
-               if (IS_ERR(keyring)) {
-                       ret = PTR_ERR(keyring);
-                       goto error;
-               }
-
-               /* attach keyring */
-               spin_lock_irq(&tsk->sighand->siglock);
-               if (!tsk->signal->process_keyring) {
-                       tsk->signal->process_keyring = keyring;
-                       keyring = NULL;
-               }
-               spin_unlock_irq(&tsk->sighand->siglock);
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
 
-               key_put(keyring);
+       ret = install_process_keyring_to_cred(new);
+       if (ret < 0) {
+               abort_creds(new);
+               return ret != -EEXIST ?: 0;
        }
 
-       ret = 0;
-error:
-       return ret;
-
-} /* end install_process_keyring() */
+       return commit_creds(new);
+}
 
-/*****************************************************************************/
 /*
- * install a session keyring, discarding the old one
- * - if a keyring is not supplied, an empty one is invented
+ * install a session keyring directly to a credentials struct
  */
-static int install_session_keyring(struct task_struct *tsk,
-                                  struct key *keyring)
+static int install_session_keyring_to_cred(struct cred *cred,
+                                          struct key *keyring)
 {
        unsigned long flags;
        struct key *old;
-       char buf[20];
 
        might_sleep();
 
        /* create an empty session keyring */
        if (!keyring) {
-               sprintf(buf, "_ses.%u", tsk->tgid);
-
                flags = KEY_ALLOC_QUOTA_OVERRUN;
-               if (tsk->signal->session_keyring)
+               if (cred->tgcred->session_keyring)
                        flags = KEY_ALLOC_IN_QUOTA;
 
-               keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
-                                       flags, NULL);
+               keyring = keyring_alloc("_ses", cred->uid, cred->gid,
+                                       cred, flags, NULL);
                if (IS_ERR(keyring))
                        return PTR_ERR(keyring);
-       }
-       else {
+       } else {
                atomic_inc(&keyring->usage);
        }
 
        /* install the keyring */
-       spin_lock_irq(&tsk->sighand->siglock);
-       old = tsk->signal->session_keyring;
-       rcu_assign_pointer(tsk->signal->session_keyring, keyring);
-       spin_unlock_irq(&tsk->sighand->siglock);
+       spin_lock_irq(&cred->tgcred->lock);
+       old = cred->tgcred->session_keyring;
+       rcu_assign_pointer(cred->tgcred->session_keyring, keyring);
+       spin_unlock_irq(&cred->tgcred->lock);
 
        /* we're using RCU on the pointer, but there's no point synchronising
         * on it if it didn't previously point to anything */
@@ -258,110 +250,29 @@ static int install_session_keyring(struct task_struct *tsk,
        }
 
        return 0;
+}
 
-} /* end install_session_keyring() */
-
-/*****************************************************************************/
-/*
- * copy the keys in a thread group for fork without CLONE_THREAD
- */
-int copy_thread_group_keys(struct task_struct *tsk)
-{
-       key_check(current->thread_group->session_keyring);
-       key_check(current->thread_group->process_keyring);
-
-       /* no process keyring yet */
-       tsk->signal->process_keyring = NULL;
-
-       /* same session keyring */
-       rcu_read_lock();
-       tsk->signal->session_keyring =
-               key_get(rcu_dereference(current->signal->session_keyring));
-       rcu_read_unlock();
-
-       return 0;
-
-} /* end copy_thread_group_keys() */
-
-/*****************************************************************************/
-/*
- * copy the keys for fork
- */
-int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
-{
-       key_check(tsk->thread_keyring);
-       key_check(tsk->request_key_auth);
-
-       /* no thread keyring yet */
-       tsk->thread_keyring = NULL;
-
-       /* copy the request_key() authorisation for this thread */
-       key_get(tsk->request_key_auth);
-
-       return 0;
-
-} /* end copy_keys() */
-
-/*****************************************************************************/
-/*
- * dispose of thread group keys upon thread group destruction
- */
-void exit_thread_group_keys(struct signal_struct *tg)
-{
-       key_put(tg->session_keyring);
-       key_put(tg->process_keyring);
-
-} /* end exit_thread_group_keys() */
-
-/*****************************************************************************/
-/*
- * dispose of per-thread keys upon thread exit
- */
-void exit_keys(struct task_struct *tsk)
-{
-       key_put(tsk->thread_keyring);
-       key_put(tsk->request_key_auth);
-
-} /* end exit_keys() */
-
-/*****************************************************************************/
 /*
- * deal with execve()
+ * install a session keyring, discarding the old one
+ * - if a keyring is not supplied, an empty one is invented
  */
-int exec_keys(struct task_struct *tsk)
+static int install_session_keyring(struct key *keyring)
 {
-       struct key *old;
-
-       /* newly exec'd tasks don't get a thread keyring */
-       task_lock(tsk);
-       old = tsk->thread_keyring;
-       tsk->thread_keyring = NULL;
-       task_unlock(tsk);
-
-       key_put(old);
-
-       /* discard the process keyring from a newly exec'd task */
-       spin_lock_irq(&tsk->sighand->siglock);
-       old = tsk->signal->process_keyring;
-       tsk->signal->process_keyring = NULL;
-       spin_unlock_irq(&tsk->sighand->siglock);
-
-       key_put(old);
-
-       return 0;
+       struct cred *new;
+       int ret;
 
-} /* end exec_keys() */
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
 
-/*****************************************************************************/
-/*
- * deal with SUID programs
- * - we might want to make this invent a new session keyring
- */
-int suid_keys(struct task_struct *tsk)
-{
-       return 0;
+       ret = install_session_keyring_to_cred(new, NULL);
+       if (ret < 0) {
+               abort_creds(new);
+               return ret;
+       }
 
-} /* end suid_keys() */
+       return commit_creds(new);
+}
 
 /*****************************************************************************/
 /*
@@ -370,10 +281,11 @@ int suid_keys(struct task_struct *tsk)
 void key_fsuid_changed(struct task_struct *tsk)
 {
        /* update the ownership of the thread keyring */
-       if (tsk->thread_keyring) {
-               down_write(&tsk->thread_keyring->sem);
-               tsk->thread_keyring->uid = tsk->fsuid;
-               up_write(&tsk->thread_keyring->sem);
+       BUG_ON(!tsk->cred);
+       if (tsk->cred->thread_keyring) {
+               down_write(&tsk->cred->thread_keyring->sem);
+               tsk->cred->thread_keyring->uid = tsk->cred->fsuid;
+               up_write(&tsk->cred->thread_keyring->sem);
        }
 
 } /* end key_fsuid_changed() */
@@ -385,10 +297,11 @@ void key_fsuid_changed(struct task_struct *tsk)
 void key_fsgid_changed(struct task_struct *tsk)
 {
        /* update the ownership of the thread keyring */
-       if (tsk->thread_keyring) {
-               down_write(&tsk->thread_keyring->sem);
-               tsk->thread_keyring->gid = tsk->fsgid;
-               up_write(&tsk->thread_keyring->sem);
+       BUG_ON(!tsk->cred);
+       if (tsk->cred->thread_keyring) {
+               down_write(&tsk->cred->thread_keyring->sem);
+               tsk->cred->thread_keyring->gid = tsk->cred->fsgid;
+               up_write(&tsk->cred->thread_keyring->sem);
        }
 
 } /* end key_fsgid_changed() */
@@ -404,7 +317,7 @@ void key_fsgid_changed(struct task_struct *tsk)
 key_ref_t search_process_keyrings(struct key_type *type,
                                  const void *description,
                                  key_match_func_t match,
-                                 struct task_struct *context)
+                                 const struct cred *cred)
 {
        struct request_key_auth *rka;
        key_ref_t key_ref, ret, err;
@@ -423,10 +336,10 @@ key_ref_t search_process_keyrings(struct key_type *type,
        err = ERR_PTR(-EAGAIN);
 
        /* search the thread keyring first */
-       if (context->thread_keyring) {
+       if (cred->thread_keyring) {
                key_ref = keyring_search_aux(
-                       make_key_ref(context->thread_keyring, 1),
-                       context, type, description, match);
+                       make_key_ref(cred->thread_keyring, 1),
+                       cred, type, description, match);
                if (!IS_ERR(key_ref))
                        goto found;
 
@@ -444,10 +357,10 @@ key_ref_t search_process_keyrings(struct key_type *type,
        }
 
        /* search the process keyring second */
-       if (context->signal->process_keyring) {
+       if (cred->tgcred->process_keyring) {
                key_ref = keyring_search_aux(
-                       make_key_ref(context->signal->process_keyring, 1),
-                       context, type, description, match);
+                       make_key_ref(cred->tgcred->process_keyring, 1),
+                       cred, type, description, match);
                if (!IS_ERR(key_ref))
                        goto found;
 
@@ -465,13 +378,13 @@ key_ref_t search_process_keyrings(struct key_type *type,
        }
 
        /* search the session keyring */
-       if (context->signal->session_keyring) {
+       if (cred->tgcred->session_keyring) {
                rcu_read_lock();
                key_ref = keyring_search_aux(
                        make_key_ref(rcu_dereference(
-                                            context->signal->session_keyring),
+                                            cred->tgcred->session_keyring),
                                     1),
-                       context, type, description, match);
+                       cred, type, description, match);
                rcu_read_unlock();
 
                if (!IS_ERR(key_ref))
@@ -490,10 +403,10 @@ key_ref_t search_process_keyrings(struct key_type *type,
                }
        }
        /* or search the user-session keyring */
-       else if (context->user->session_keyring) {
+       else if (cred->user->session_keyring) {
                key_ref = keyring_search_aux(
-                       make_key_ref(context->user->session_keyring, 1),
-                       context, type, description, match);
+                       make_key_ref(cred->user->session_keyring, 1),
+                       cred, type, description, match);
                if (!IS_ERR(key_ref))
                        goto found;
 
@@ -514,20 +427,20 @@ key_ref_t search_process_keyrings(struct key_type *type,
         * search the keyrings of the process mentioned there
         * - we don't permit access to request_key auth keys via this method
         */
-       if (context->request_key_auth &&
-           context == current &&
+       if (cred->request_key_auth &&
+           cred == current_cred() &&
            type != &key_type_request_key_auth
            ) {
                /* defend against the auth key being revoked */
-               down_read(&context->request_key_auth->sem);
+               down_read(&cred->request_key_auth->sem);
 
-               if (key_validate(context->request_key_auth) == 0) {
-                       rka = context->request_key_auth->payload.data;
+               if (key_validate(cred->request_key_auth) == 0) {
+                       rka = cred->request_key_auth->payload.data;
 
                        key_ref = search_process_keyrings(type, description,
-                                                         match, rka->context);
+                                                         match, rka->cred);
 
-                       up_read(&context->request_key_auth->sem);
+                       up_read(&cred->request_key_auth->sem);
 
                        if (!IS_ERR(key_ref))
                                goto found;
@@ -544,7 +457,7 @@ key_ref_t search_process_keyrings(struct key_type *type,
                                break;
                        }
                } else {
-                       up_read(&context->request_key_auth->sem);
+                       up_read(&cred->request_key_auth->sem);
                }
        }
 
@@ -572,93 +485,98 @@ static int lookup_user_key_possessed(const struct key *key, const void *target)
  * - don't create special keyrings unless so requested
  * - partially constructed keys aren't found unless requested
  */
-key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
-                         int create, int partial, key_perm_t perm)
+key_ref_t lookup_user_key(key_serial_t id, int create, int partial,
+                         key_perm_t perm)
 {
-       key_ref_t key_ref, skey_ref;
+       struct request_key_auth *rka;
+       const struct cred *cred;
        struct key *key;
+       key_ref_t key_ref, skey_ref;
        int ret;
 
-       if (!context)
-               context = current;
-
+try_again:
+       cred = get_current_cred();
        key_ref = ERR_PTR(-ENOKEY);
 
        switch (id) {
        case KEY_SPEC_THREAD_KEYRING:
-               if (!context->thread_keyring) {
+               if (!cred->thread_keyring) {
                        if (!create)
                                goto error;
 
-                       ret = install_thread_keyring(context);
+                       ret = install_thread_keyring();
                        if (ret < 0) {
                                key = ERR_PTR(ret);
                                goto error;
                        }
+                       goto reget_creds;
                }
 
-               key = context->thread_keyring;
+               key = cred->thread_keyring;
                atomic_inc(&key->usage);
                key_ref = make_key_ref(key, 1);
                break;
 
        case KEY_SPEC_PROCESS_KEYRING:
-               if (!context->signal->process_keyring) {
+               if (!cred->tgcred->process_keyring) {
                        if (!create)
                                goto error;
 
-                       ret = install_process_keyring(context);
+                       ret = install_process_keyring();
                        if (ret < 0) {
                                key = ERR_PTR(ret);
                                goto error;
                        }
+                       goto reget_creds;
                }
 
-               key = context->signal->process_keyring;
+               key = cred->tgcred->process_keyring;
                atomic_inc(&key->usage);
                key_ref = make_key_ref(key, 1);
                break;
 
        case KEY_SPEC_SESSION_KEYRING:
-               if (!context->signal->session_keyring) {
+               if (!cred->tgcred->session_keyring) {
                        /* always install a session keyring upon access if one
                         * doesn't exist yet */
-                       ret = install_user_keyrings(context);
+                       ret = install_user_keyrings();
                        if (ret < 0)
                                goto error;
                        ret = install_session_keyring(
-                               context, context->user->session_keyring);
+                               cred->user->session_keyring);
+
                        if (ret < 0)
                                goto error;
+                       goto reget_creds;
                }
 
                rcu_read_lock();
-               key = rcu_dereference(context->signal->session_keyring);
+               key = rcu_dereference(cred->tgcred->session_keyring);
                atomic_inc(&key->usage);
                rcu_read_unlock();
                key_ref = make_key_ref(key, 1);
                break;
 
        case KEY_SPEC_USER_KEYRING:
-               if (!context->user->uid_keyring) {
-                       ret = install_user_keyrings(context);
+               if (!cred->user->uid_keyring) {
+                       ret = install_user_keyrings();
                        if (ret < 0)
                                goto error;
                }
 
-               key = context->user->uid_keyring;
+               key = cred->user->uid_keyring;
                atomic_inc(&key->usage);
                key_ref = make_key_ref(key, 1);
                break;
 
        case KEY_SPEC_USER_SESSION_KEYRING:
-               if (!context->user->session_keyring) {
-                       ret = install_user_keyrings(context);
+               if (!cred->user->session_keyring) {
+                       ret = install_user_keyrings();
                        if (ret < 0)
                                goto error;
                }
 
-               key = context->user->session_keyring;
+               key = cred->user->session_keyring;
                atomic_inc(&key->usage);
                key_ref = make_key_ref(key, 1);
                break;
@@ -669,7 +587,7 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
                goto error;
 
        case KEY_SPEC_REQKEY_AUTH_KEY:
-               key = context->request_key_auth;
+               key = cred->request_key_auth;
                if (!key)
                        goto error;
 
@@ -677,6 +595,25 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
                key_ref = make_key_ref(key, 1);
                break;
 
+       case KEY_SPEC_REQUESTOR_KEYRING:
+               if (!cred->request_key_auth)
+                       goto error;
+
+               down_read(&cred->request_key_auth->sem);
+               if (cred->request_key_auth->flags & KEY_FLAG_REVOKED) {
+                       key_ref = ERR_PTR(-EKEYREVOKED);
+                       key = NULL;
+               } else {
+                       rka = cred->request_key_auth->payload.data;
+                       key = rka->dest_keyring;
+                       atomic_inc(&key->usage);
+               }
+               up_read(&cred->request_key_auth->sem);
+               if (!key)
+                       goto error;
+               key_ref = make_key_ref(key, 1);
+               break;
+
        default:
                key_ref = ERR_PTR(-EINVAL);
                if (id < 1)
@@ -693,7 +630,7 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
                /* check to see if we possess the key */
                skey_ref = search_process_keyrings(key->type, key,
                                                   lookup_user_key_possessed,
-                                                  current);
+                                                  cred);
 
                if (!IS_ERR(skey_ref)) {
                        key_put(key);
@@ -725,11 +662,12 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
                goto invalid_key;
 
        /* check the permissions */
-       ret = key_task_permission(key_ref, context, perm);
+       ret = key_task_permission(key_ref, cred, perm);
        if (ret < 0)
                goto invalid_key;
 
 error:
+       put_cred(cred);
        return key_ref;
 
 invalid_key:
@@ -737,6 +675,12 @@ invalid_key:
        key_ref = ERR_PTR(ret);
        goto error;
 
+       /* if we attempted to install a keyring, then it may have caused new
+        * creds to be installed */
+reget_creds:
+       put_cred(cred);
+       goto try_again;
+
 } /* end lookup_user_key() */
 
 /*****************************************************************************/
@@ -748,20 +692,33 @@ invalid_key:
  */
 long join_session_keyring(const char *name)
 {
-       struct task_struct *tsk = current;
+       const struct cred *old;
+       struct cred *new;
        struct key *keyring;
-       long ret;
+       long ret, serial;
+
+       /* only permit this if there's a single thread in the thread group -
+        * this avoids us having to adjust the creds on all threads and risking
+        * ENOMEM */
+       if (!is_single_threaded(current))
+               return -EMLINK;
+
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+       old = current_cred();
 
        /* if no name is provided, install an anonymous keyring */
        if (!name) {
-               ret = install_session_keyring(tsk, NULL);
+               ret = install_session_keyring_to_cred(new, NULL);
                if (ret < 0)
                        goto error;
 
-               rcu_read_lock();
-               ret = rcu_dereference(tsk->signal->session_keyring)->serial;
-               rcu_read_unlock();
-               goto error;
+               serial = new->tgcred->session_keyring->serial;
+               ret = commit_creds(new);
+               if (ret == 0)
+                       ret = serial;
+               goto okay;
        }
 
        /* allow the user to join or create a named keyring */
@@ -771,29 +728,33 @@ long join_session_keyring(const char *name)
        keyring = find_keyring_by_name(name, false);
        if (PTR_ERR(keyring) == -ENOKEY) {
                /* not found - try and create a new one */
-               keyring = keyring_alloc(name, tsk->uid, tsk->gid, tsk,
+               keyring = keyring_alloc(name, old->uid, old->gid, old,
                                        KEY_ALLOC_IN_QUOTA, NULL);
                if (IS_ERR(keyring)) {
                        ret = PTR_ERR(keyring);
                        goto error2;
                }
-       }
-       else if (IS_ERR(keyring)) {
+       } else if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
                goto error2;
        }
 
        /* we've got a keyring - now to install it */
-       ret = install_session_keyring(tsk, keyring);
+       ret = install_session_keyring_to_cred(new, keyring);
        if (ret < 0)
                goto error2;
 
+       commit_creds(new);
+       mutex_unlock(&key_session_mutex);
+
        ret = keyring->serial;
        key_put(keyring);
+okay:
+       return ret;
 
 error2:
        mutex_unlock(&key_session_mutex);
 error:
+       abort_creds(new);
        return ret;
-
-} /* end join_session_keyring() */
+}
index abea08f..0e04f72 100644 (file)
@@ -19,6 +19,8 @@
 #include <linux/slab.h>
 #include "internal.h"
 
+#define key_negative_timeout   60      /* default timeout on a negative key's existence */
+
 /*
  * wait_on_bit() sleep function for uninterruptible waiting
  */
@@ -64,7 +66,7 @@ static int call_sbin_request_key(struct key_construction *cons,
                                 const char *op,
                                 void *aux)
 {
-       struct task_struct *tsk = current;
+       const struct cred *cred = current_cred();
        key_serial_t prkey, sskey;
        struct key *key = cons->key, *authkey = cons->authkey, *keyring;
        char *argv[9], *envp[3], uid_str[12], gid_str[12];
@@ -74,15 +76,17 @@ static int call_sbin_request_key(struct key_construction *cons,
 
        kenter("{%d},{%d},%s", key->serial, authkey->serial, op);
 
-       ret = install_user_keyrings(tsk);
+       ret = install_user_keyrings();
        if (ret < 0)
                goto error_alloc;
 
        /* allocate a new session keyring */
        sprintf(desc, "_req.%u", key->serial);
 
-       keyring = keyring_alloc(desc, current->fsuid, current->fsgid, current,
+       cred = get_current_cred();
+       keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred,
                                KEY_ALLOC_QUOTA_OVERRUN, NULL);
+       put_cred(cred);
        if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
                goto error_alloc;
@@ -94,29 +98,24 @@ static int call_sbin_request_key(struct key_construction *cons,
                goto error_link;
 
        /* record the UID and GID */
-       sprintf(uid_str, "%d", current->fsuid);
-       sprintf(gid_str, "%d", current->fsgid);
+       sprintf(uid_str, "%d", cred->fsuid);
+       sprintf(gid_str, "%d", cred->fsgid);
 
        /* we say which key is under construction */
        sprintf(key_str, "%d", key->serial);
 
        /* we specify the process's default keyrings */
        sprintf(keyring_str[0], "%d",
-               tsk->thread_keyring ? tsk->thread_keyring->serial : 0);
+               cred->thread_keyring ? cred->thread_keyring->serial : 0);
 
        prkey = 0;
-       if (tsk->signal->process_keyring)
-               prkey = tsk->signal->process_keyring->serial;
-
-       sprintf(keyring_str[1], "%d", prkey);
+       if (cred->tgcred->process_keyring)
+               prkey = cred->tgcred->process_keyring->serial;
 
-       if (tsk->signal->session_keyring) {
-               rcu_read_lock();
-               sskey = rcu_dereference(tsk->signal->session_keyring)->serial;
-               rcu_read_unlock();
-       } else {
-               sskey = tsk->user->session_keyring->serial;
-       }
+       if (cred->tgcred->session_keyring)
+               sskey = rcu_dereference(cred->tgcred->session_keyring)->serial;
+       else
+               sskey = cred->user->session_keyring->serial;
 
        sprintf(keyring_str[2], "%d", sskey);
 
@@ -157,8 +156,8 @@ error_link:
        key_put(keyring);
 
 error_alloc:
-       kleave(" = %d", ret);
        complete_request_key(cons, ret);
+       kleave(" = %d", ret);
        return ret;
 }
 
@@ -167,7 +166,8 @@ error_alloc:
  * - we ignore program failure and go on key status instead
  */
 static int construct_key(struct key *key, const void *callout_info,
-                        size_t callout_len, void *aux)
+                        size_t callout_len, void *aux,
+                        struct key *dest_keyring)
 {
        struct key_construction *cons;
        request_key_actor_t actor;
@@ -181,7 +181,8 @@ static int construct_key(struct key *key, const void *callout_info,
                return -ENOMEM;
 
        /* allocate an authorisation key */
-       authkey = request_key_auth_new(key, callout_info, callout_len);
+       authkey = request_key_auth_new(key, callout_info, callout_len,
+                                      dest_keyring);
        if (IS_ERR(authkey)) {
                kfree(cons);
                ret = PTR_ERR(authkey);
@@ -209,46 +210,67 @@ static int construct_key(struct key *key, const void *callout_info,
 }
 
 /*
- * link a key to the appropriate destination keyring
- * - the caller must hold a write lock on the destination keyring
+ * get the appropriate destination keyring for the request
+ * - we return whatever keyring we select with an extra reference upon it which
+ *   the caller must release
  */
-static void construct_key_make_link(struct key *key, struct key *dest_keyring)
+static void construct_get_dest_keyring(struct key **_dest_keyring)
 {
-       struct task_struct *tsk = current;
-       struct key *drop = NULL;
+       struct request_key_auth *rka;
+       const struct cred *cred = current_cred();
+       struct key *dest_keyring = *_dest_keyring, *authkey;
 
-       kenter("{%d},%p", key->serial, dest_keyring);
+       kenter("%p", dest_keyring);
 
        /* find the appropriate keyring */
-       if (!dest_keyring) {
-               switch (tsk->jit_keyring) {
+       if (dest_keyring) {
+               /* the caller supplied one */
+               key_get(dest_keyring);
+       } else {
+               /* use a default keyring; falling through the cases until we
+                * find one that we actually have */
+               switch (cred->jit_keyring) {
                case KEY_REQKEY_DEFL_DEFAULT:
+               case KEY_REQKEY_DEFL_REQUESTOR_KEYRING:
+                       if (cred->request_key_auth) {
+                               authkey = cred->request_key_auth;
+                               down_read(&authkey->sem);
+                               rka = authkey->payload.data;
+                               if (!test_bit(KEY_FLAG_REVOKED,
+                                             &authkey->flags))
+                                       dest_keyring =
+                                               key_get(rka->dest_keyring);
+                               up_read(&authkey->sem);
+                               if (dest_keyring)
+                                       break;
+                       }
+
                case KEY_REQKEY_DEFL_THREAD_KEYRING:
-                       dest_keyring = tsk->thread_keyring;
+                       dest_keyring = key_get(cred->thread_keyring);
                        if (dest_keyring)
                                break;
 
                case KEY_REQKEY_DEFL_PROCESS_KEYRING:
-                       dest_keyring = tsk->signal->process_keyring;
+                       dest_keyring = key_get(cred->tgcred->process_keyring);
                        if (dest_keyring)
                                break;
 
                case KEY_REQKEY_DEFL_SESSION_KEYRING:
                        rcu_read_lock();
                        dest_keyring = key_get(
-                               rcu_dereference(tsk->signal->session_keyring));
+                               rcu_dereference(cred->tgcred->session_keyring));
                        rcu_read_unlock();
-                       drop = dest_keyring;
 
                        if (dest_keyring)
                                break;
 
                case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
-                       dest_keyring = tsk->user->session_keyring;
+                       dest_keyring =
+                               key_get(cred->user->session_keyring);
                        break;
 
                case KEY_REQKEY_DEFL_USER_KEYRING:
-                       dest_keyring = tsk->user->uid_keyring;
+                       dest_keyring = key_get(cred->user->uid_keyring);
                        break;
 
                case KEY_REQKEY_DEFL_GROUP_KEYRING:
@@ -257,10 +279,9 @@ static void construct_key_make_link(struct key *key, struct key *dest_keyring)
                }
        }
 
-       /* and attach the key to it */
-       __key_link(dest_keyring, key);
-       key_put(drop);
-       kleave("");
+       *_dest_keyring = dest_keyring;
+       kleave(" [dk %d]", key_serial(dest_keyring));
+       return;
 }
 
 /*
@@ -275,6 +296,7 @@ static int construct_alloc_key(struct key_type *type,
                               struct key_user *user,
                               struct key **_key)
 {
+       const struct cred *cred = current_cred();
        struct key *key;
        key_ref_t key_ref;
 
@@ -282,33 +304,28 @@ static int construct_alloc_key(struct key_type *type,
 
        mutex_lock(&user->cons_lock);
 
-       key = key_alloc(type, description,
-                       current->fsuid, current->fsgid, current, KEY_POS_ALL,
-                       flags);
+       key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred,
+                       KEY_POS_ALL, flags);
        if (IS_ERR(key))
                goto alloc_failed;
 
        set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
 
-       if (dest_keyring)
-               down_write(&dest_keyring->sem);
+       down_write(&dest_keyring->sem);
 
        /* attach the key to the destination keyring under lock, but we do need
         * to do another check just in case someone beat us to it whilst we
         * waited for locks */
        mutex_lock(&key_construction_mutex);
 
-       key_ref = search_process_keyrings(type, description, type->match,
-                                         current);
+       key_ref = search_process_keyrings(type, description, type->match, cred);
        if (!IS_ERR(key_ref))
                goto key_already_present;
 
-       if (dest_keyring)
-               construct_key_make_link(key, dest_keyring);
+       __key_link(dest_keyring, key);
 
        mutex_unlock(&key_construction_mutex);
-       if (dest_keyring)
-               up_write(&dest_keyring->sem);
+       up_write(&dest_keyring->sem);
        mutex_unlock(&user->cons_lock);
        *_key = key;
        kleave(" = 0 [%d]", key_serial(key));
@@ -346,25 +363,36 @@ static struct key *construct_key_and_link(struct key_type *type,
        struct key *key;
        int ret;
 
-       user = key_user_lookup(current->fsuid);
+       kenter("");
+
+       user = key_user_lookup(current_fsuid());
        if (!user)
                return ERR_PTR(-ENOMEM);
 
+       construct_get_dest_keyring(&dest_keyring);
+
        ret = construct_alloc_key(type, description, dest_keyring, flags, user,
                                  &key);
        key_user_put(user);
 
        if (ret == 0) {
-               ret = construct_key(key, callout_info, callout_len, aux);
-               if (ret < 0)
+               ret = construct_key(key, callout_info, callout_len, aux,
+                                   dest_keyring);
+               if (ret < 0) {
+                       kdebug("cons failed");
                        goto construction_failed;
+               }
        }
 
+       key_put(dest_keyring);
+       kleave(" = key %d", key_serial(key));
        return key;
 
 construction_failed:
        key_negate_and_link(key, key_negative_timeout, NULL, NULL);
        key_put(key);
+       key_put(dest_keyring);
+       kleave(" = %d", ret);
        return ERR_PTR(ret);
 }
 
@@ -383,6 +411,7 @@ struct key *request_key_and_link(struct key_type *type,
                                 struct key *dest_keyring,
                                 unsigned long flags)
 {
+       const struct cred *cred = current_cred();
        struct key *key;
        key_ref_t key_ref;
 
@@ -392,7 +421,7 @@ struct key *request_key_and_link(struct key_type *type,
 
        /* search all the process keyrings for a key */
        key_ref = search_process_keyrings(type, description, type->match,
-                                         current);
+                                         cred);
 
        if (!IS_ERR(key_ref)) {
                key = key_ref_to_ptr(key_ref);
index bd237b0..8674715 100644 (file)
@@ -105,9 +105,9 @@ static void request_key_auth_revoke(struct key *key)
 
        kenter("{%d}", key->serial);
 
-       if (rka->context) {
-               put_task_struct(rka->context);
-               rka->context = NULL;
+       if (rka->cred) {
+               put_cred(rka->cred);
+               rka->cred = NULL;
        }
 
 } /* end request_key_auth_revoke() */
@@ -122,12 +122,13 @@ static void request_key_auth_destroy(struct key *key)
 
        kenter("{%d}", key->serial);
 
-       if (rka->context) {
-               put_task_struct(rka->context);
-               rka->context = NULL;
+       if (rka->cred) {
+               put_cred(rka->cred);
+               rka->cred = NULL;
        }
 
        key_put(rka->target_key);
+       key_put(rka->dest_keyring);
        kfree(rka->callout_info);
        kfree(rka);
 
@@ -139,9 +140,10 @@ static void request_key_auth_destroy(struct key *key)
  * access to the caller's security data
  */
 struct key *request_key_auth_new(struct key *target, const void *callout_info,
-                                size_t callout_len)
+                                size_t callout_len, struct key *dest_keyring)
 {
        struct request_key_auth *rka, *irka;
+       const struct cred *cred = current->cred;
        struct key *authkey = NULL;
        char desc[20];
        int ret;
@@ -163,31 +165,29 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
 
        /* see if the calling process is already servicing the key request of
         * another process */
-       if (current->request_key_auth) {
+       if (cred->request_key_auth) {
                /* it is - use that instantiation context here too */
-               down_read(&current->request_key_auth->sem);
+               down_read(&cred->request_key_auth->sem);
 
                /* if the auth key has been revoked, then the key we're
                 * servicing is already instantiated */
-               if (test_bit(KEY_FLAG_REVOKED,
-                            &current->request_key_auth->flags))
+               if (test_bit(KEY_FLAG_REVOKED, &cred->request_key_auth->flags))
                        goto auth_key_revoked;
 
-               irka = current->request_key_auth->payload.data;
-               rka->context = irka->context;
+               irka = cred->request_key_auth->payload.data;
+               rka->cred = get_cred(irka->cred);
                rka->pid = irka->pid;
-               get_task_struct(rka->context);
 
-               up_read(&current->request_key_auth->sem);
+               up_read(&cred->request_key_auth->sem);
        }
        else {
                /* it isn't - use this process as the context */
-               rka->context = current;
+               rka->cred = get_cred(cred);
                rka->pid = current->pid;
-               get_task_struct(rka->context);
        }
 
        rka->target_key = key_get(target);
+       rka->dest_keyring = key_get(dest_keyring);
        memcpy(rka->callout_info, callout_info, callout_len);
        rka->callout_len = callout_len;
 
@@ -195,7 +195,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
        sprintf(desc, "%x", target->serial);
 
        authkey = key_alloc(&key_type_request_key_auth, desc,
-                           current->fsuid, current->fsgid, current,
+                           cred->fsuid, cred->fsgid, cred,
                            KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
                            KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA);
        if (IS_ERR(authkey)) {
@@ -203,16 +203,16 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
                goto error_alloc;
        }
 
-       /* construct and attach to the keyring */
+       /* construct the auth key */
        ret = key_instantiate_and_link(authkey, rka, 0, NULL, NULL);
        if (ret < 0)
                goto error_inst;
 
-       kleave(" = {%d}", authkey->serial);
+       kleave(" = {%d,%d}", authkey->serial, atomic_read(&authkey->usage));
        return authkey;
 
 auth_key_revoked:
-       up_read(&current->request_key_auth->sem);
+       up_read(&cred->request_key_auth->sem);
        kfree(rka->callout_info);
        kfree(rka);
        kleave("= -EKEYREVOKED");
@@ -223,6 +223,7 @@ error_inst:
        key_put(authkey);
 error_alloc:
        key_put(rka->target_key);
+       key_put(rka->dest_keyring);
        kfree(rka->callout_info);
        kfree(rka);
        kleave("= %d", ret);
@@ -254,6 +255,7 @@ static int key_get_instantiation_authkey_match(const struct key *key,
  */
 struct key *key_get_instantiation_authkey(key_serial_t target_id)
 {
+       const struct cred *cred = current_cred();
        struct key *authkey;
        key_ref_t authkey_ref;
 
@@ -261,7 +263,7 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id)
                &key_type_request_key_auth,
                (void *) (unsigned long) target_id,
                key_get_instantiation_authkey_match,
-               current);
+               cred);
 
        if (IS_ERR(authkey_ref)) {
                authkey = ERR_CAST(authkey_ref);
index c3f68b5..40fb4f1 100644 (file)
@@ -55,9 +55,9 @@ static int rootplug_bprm_check_security (struct linux_binprm *bprm)
        struct usb_device *dev;
 
        root_dbg("file %s, e_uid = %d, e_gid = %d\n",
-                bprm->filename, bprm->e_uid, bprm->e_gid);
+                bprm->filename, bprm->cred->euid, bprm->cred->egid);
 
-       if (bprm->e_gid == 0) {
+       if (bprm->cred->egid == 0) {
                dev = usb_find_device(vendor_id, product_id);
                if (!dev) {
                        root_dbg("e_gid = 0, and device not found, "
@@ -75,15 +75,12 @@ static struct security_operations rootplug_security_ops = {
        .ptrace_may_access =            cap_ptrace_may_access,
        .ptrace_traceme =               cap_ptrace_traceme,
        .capget =                       cap_capget,
-       .capset_check =                 cap_capset_check,
-       .capset_set =                   cap_capset_set,
+       .capset =                       cap_capset,
        .capable =                      cap_capable,
 
-       .bprm_apply_creds =             cap_bprm_apply_creds,
-       .bprm_set_security =            cap_bprm_set_security,
+       .bprm_set_creds =               cap_bprm_set_creds,
 
-       .task_post_setuid =             cap_task_post_setuid,
-       .task_reparent_to_init =        cap_task_reparent_to_init,
+       .task_fix_setuid =              cap_task_fix_setuid,
        .task_prctl =                   cap_task_prctl,
 
        .bprm_check_security =          rootplug_bprm_check_security,
index c0acfa7..d85dbb3 100644 (file)
@@ -145,25 +145,23 @@ int security_capget(struct task_struct *target,
        return security_ops->capget(target, effective, inheritable, permitted);
 }
 
-int security_capset_check(struct task_struct *target,
-                          kernel_cap_t *effective,
-                          kernel_cap_t *inheritable,
-                          kernel_cap_t *permitted)
+int security_capset(struct cred *new, const struct cred *old,
+                   const kernel_cap_t *effective,
+                   const kernel_cap_t *inheritable,
+                   const kernel_cap_t *permitted)
 {
-       return security_ops->capset_check(target, effective, inheritable, permitted);
+       return security_ops->capset(new, old,
+                                   effective, inheritable, permitted);
 }
 
-void security_capset_set(struct task_struct *target,
-                         kernel_cap_t *effective,
-                         kernel_cap_t *inheritable,
-                         kernel_cap_t *permitted)
+int security_capable(struct task_struct *tsk, int cap)
 {
-       security_ops->capset_set(target, effective, inheritable, permitted);
+       return security_ops->capable(tsk, cap, SECURITY_CAP_AUDIT);
 }
 
-int security_capable(struct task_struct *tsk, int cap)
+int security_capable_noaudit(struct task_struct *tsk, int cap)
 {
-       return security_ops->capable(tsk, cap);
+       return security_ops->capable(tsk, cap, SECURITY_CAP_NOAUDIT);
 }
 
 int security_acct(struct file *file)
@@ -215,34 +213,24 @@ int security_vm_enough_memory_kern(long pages)
        return security_ops->vm_enough_memory(current->mm, pages);
 }
 
-int security_bprm_alloc(struct linux_binprm *bprm)
-{
-       return security_ops->bprm_alloc_security(bprm);
-}
-
-void security_bprm_free(struct linux_binprm *bprm)
-{
-       security_ops->bprm_free_security(bprm);
-}
-
-void security_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
+int security_bprm_set_creds(struct linux_binprm *bprm)
 {
-       security_ops->bprm_apply_creds(bprm, unsafe);
+       return security_ops->bprm_set_creds(bprm);
 }
 
-void security_bprm_post_apply_creds(struct linux_binprm *bprm)
+int security_bprm_check(struct linux_binprm *bprm)
 {
-       security_ops->bprm_post_apply_creds(bprm);
+       return security_ops->bprm_check_security(bprm);
 }
 
-int security_bprm_set(struct linux_binprm *bprm)
+void security_bprm_committing_creds(struct linux_binprm *bprm)
 {
-       return security_ops->bprm_set_security(bprm);
+       security_ops->bprm_committing_creds(bprm);
 }
 
-int security_bprm_check(struct linux_binprm *bprm)
+void security_bprm_committed_creds(struct linux_binprm *bprm)
 {
-       return security_ops->bprm_check_security(bprm);
+       security_ops->bprm_committed_creds(bprm);
 }
 
 int security_bprm_secureexec(struct linux_binprm *bprm)
@@ -266,9 +254,9 @@ int security_sb_copy_data(char *orig, char *copy)
 }
 EXPORT_SYMBOL(security_sb_copy_data);
 
-int security_sb_kern_mount(struct super_block *sb, void *data)
+int security_sb_kern_mount(struct super_block *sb, int flags, void *data)
 {
-       return security_ops->sb_kern_mount(sb, data);
+       return security_ops->sb_kern_mount(sb, flags, data);
 }
 
 int security_sb_show_options(struct seq_file *m, struct super_block *sb)
@@ -603,9 +591,9 @@ int security_file_receive(struct file *file)
        return security_ops->file_receive(file);
 }
 
-int security_dentry_open(struct file *file)
+int security_dentry_open(struct file *file, const struct cred *cred)
 {
-       return security_ops->dentry_open(file);
+       return security_ops->dentry_open(file, cred);
 }
 
 int security_task_create(unsigned long clone_flags)
@@ -613,14 +601,29 @@ int security_task_create(unsigned long clone_flags)
        return security_ops->task_create(clone_flags);
 }
 
-int security_task_alloc(struct task_struct *p)
+void security_cred_free(struct cred *cred)
 {
-       return security_ops->task_alloc_security(p);
+       security_ops->cred_free(cred);
 }
 
-void security_task_free(struct task_struct *p)
+int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
 {
-       security_ops->task_free_security(p);
+       return security_ops->cred_prepare(new, old, gfp);
+}
+
+void security_commit_creds(struct cred *new, const struct cred *old)
+{
+       security_ops->cred_commit(new, old);
+}
+
+int security_kernel_act_as(struct cred *new, u32 secid)
+{
+       return security_ops->kernel_act_as(new, secid);
+}
+
+int security_kernel_create_files_as(struct cred *new, struct inode *inode)
+{
+       return security_ops->kernel_create_files_as(new, inode);
 }
 
 int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
@@ -628,10 +631,10 @@ int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
        return security_ops->task_setuid(id0, id1, id2, flags);
 }
 
-int security_task_post_setuid(uid_t old_ruid, uid_t old_euid,
-                              uid_t old_suid, int flags)
+int security_task_fix_setuid(struct cred *new, const struct cred *old,
+                            int flags)
 {
-       return security_ops->task_post_setuid(old_ruid, old_euid, old_suid, flags);
+       return security_ops->task_fix_setuid(new, old, flags);
 }
 
 int security_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
@@ -713,14 +716,9 @@ int security_task_wait(struct task_struct *p)
 }
 
 int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
-                        unsigned long arg4, unsigned long arg5, long *rc_p)
-{
-       return security_ops->task_prctl(option, arg2, arg3, arg4, arg5, rc_p);
-}
-
-void security_task_reparent_to_init(struct task_struct *p)
+                        unsigned long arg4, unsigned long arg5)
 {
-       security_ops->task_reparent_to_init(p);
+       return security_ops->task_prctl(option, arg2, arg3, arg4, arg5);
 }
 
 void security_task_to_inode(struct task_struct *p, struct inode *inode)
@@ -1120,9 +1118,10 @@ EXPORT_SYMBOL(security_skb_classify_flow);
 
 #ifdef CONFIG_KEYS
 
-int security_key_alloc(struct key *key, struct task_struct *tsk, unsigned long flags)
+int security_key_alloc(struct key *key, const struct cred *cred,
+                      unsigned long flags)
 {
-       return security_ops->key_alloc(key, tsk, flags);
+       return security_ops->key_alloc(key, cred, flags);
 }
 
 void security_key_free(struct key *key)
@@ -1131,9 +1130,9 @@ void security_key_free(struct key *key)
 }
 
 int security_key_permission(key_ref_t key_ref,
-                           struct task_struct *context, key_perm_t perm)
+                           const struct cred *cred, key_perm_t perm)
 {
-       return security_ops->key_permission(key_ref, context, perm);
+       return security_ops->key_permission(key_ref, cred, perm);
 }
 
 int security_key_getsecurity(struct key *key, char **_buffer)
index 64af2d3..c73aeaa 100644 (file)
@@ -39,9 +39,13 @@ EXPORT_SYMBOL_GPL(selinux_string_to_sid);
 int selinux_secmark_relabel_packet_permission(u32 sid)
 {
        if (selinux_enabled) {
-               struct task_security_struct *tsec = current->security;
+               const struct task_security_struct *__tsec;
+               u32 tsid;
 
-               return avc_has_perm(tsec->sid, sid, SECCLASS_PACKET,
+               __tsec = current_security();
+               tsid = __tsec->sid;
+
+               return avc_has_perm(tsid, sid, SECCLASS_PACKET,
                                    PACKET__RELABELTO, NULL);
        }
        return 0;
index f85597a..853b58c 100644 (file)
@@ -156,33 +156,62 @@ static int selinux_secmark_enabled(void)
        return (atomic_read(&selinux_secmark_refcount) > 0);
 }
 
-/* Allocate and free functions for each kind of security blob. */
-
-static int task_alloc_security(struct task_struct *task)
+/*
+ * initialise the security for the init task
+ */
+static void cred_init_security(void)
 {
+       struct cred *cred = (struct cred *) current->real_cred;
        struct task_security_struct *tsec;
 
        tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);
        if (!tsec)
-               return -ENOMEM;
+               panic("SELinux:  Failed to initialize initial task.\n");
 
-       tsec->osid = tsec->sid = SECINITSID_UNLABELED;
-       task->security = tsec;
+       tsec->osid = tsec->sid = SECINITSID_KERNEL;
+       cred->security = tsec;
+}
 
-       return 0;
+/*
+ * get the security ID of a set of credentials
+ */
+static inline u32 cred_sid(const struct cred *cred)
+{
+       const struct task_security_struct *tsec;
+
+       tsec = cred->security;
+       return tsec->sid;
 }
 
-static void task_free_security(struct task_struct *task)
+/*
+ * get the objective security ID of a task
+ */
+static inline u32 task_sid(const struct task_struct *task)
 {
-       struct task_security_struct *tsec = task->security;
-       task->security = NULL;
-       kfree(tsec);
+       u32 sid;
+
+       rcu_read_lock();
+       sid = cred_sid(__task_cred(task));
+       rcu_read_unlock();
+       return sid;
 }
 
+/*
+ * get the subjective security ID of the current task
+ */
+static inline u32 current_sid(void)
+{
+       const struct task_security_struct *tsec = current_cred()->security;
+
+       return tsec->sid;
+}
+
+/* Allocate and free functions for each kind of security blob. */
+
 static int inode_alloc_security(struct inode *inode)
 {
-       struct task_security_struct *tsec = current->security;
        struct inode_security_struct *isec;
+       u32 sid = current_sid();
 
        isec = kmem_cache_zalloc(sel_inode_cache, GFP_NOFS);
        if (!isec)
@@ -193,7 +222,7 @@ static int inode_alloc_security(struct inode *inode)
        isec->inode = inode;
        isec->sid = SECINITSID_UNLABELED;
        isec->sclass = SECCLASS_FILE;
-       isec->task_sid = tsec->sid;
+       isec->task_sid = sid;
        inode->i_security = isec;
 
        return 0;
@@ -215,15 +244,15 @@ static void inode_free_security(struct inode *inode)
 
 static int file_alloc_security(struct file *file)
 {
-       struct task_security_struct *tsec = current->security;
        struct file_security_struct *fsec;
+       u32 sid = current_sid();
 
        fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL);
        if (!fsec)
                return -ENOMEM;
 
-       fsec->sid = tsec->sid;
-       fsec->fown_sid = tsec->sid;
+       fsec->sid = sid;
+       fsec->fown_sid = sid;
        file->f_security = fsec;
 
        return 0;
@@ -338,8 +367,9 @@ static const match_table_t tokens = {
 
 static int may_context_mount_sb_relabel(u32 sid,
                        struct superblock_security_struct *sbsec,
-                       struct task_security_struct *tsec)
+                       const struct cred *cred)
 {
+       const struct task_security_struct *tsec = cred->security;
        int rc;
 
        rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
@@ -354,8 +384,9 @@ static int may_context_mount_sb_relabel(u32 sid,
 
 static int may_context_mount_inode_relabel(u32 sid,
                        struct superblock_security_struct *sbsec,
-                       struct task_security_struct *tsec)
+                       const struct cred *cred)
 {
+       const struct task_security_struct *tsec = cred->security;
        int rc;
        rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
                          FILESYSTEM__RELABELFROM, NULL);
@@ -553,8 +584,8 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
 static int selinux_set_mnt_opts(struct super_block *sb,
                                struct security_mnt_opts *opts)
 {
+       const struct cred *cred = current_cred();
        int rc = 0, i;
-       struct task_security_struct *tsec = current->security;
        struct superblock_security_struct *sbsec = sb->s_security;
        const char *name = sb->s_type->name;
        struct inode *inode = sbsec->sb->s_root->d_inode;
@@ -671,7 +702,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
                sbsec->proc = 1;
 
        /* Determine the labeling behavior to use for this filesystem type. */
-       rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
+       rc = security_fs_use(sbsec->proc ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid);
        if (rc) {
                printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
                       __func__, sb->s_type->name, rc);
@@ -680,8 +711,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
 
        /* sets the context of the superblock for the fs being mounted. */
        if (fscontext_sid) {
-
-               rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, tsec);
+               rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred);
                if (rc)
                        goto out;
 
@@ -695,12 +725,14 @@ static int selinux_set_mnt_opts(struct super_block *sb,
         */
        if (context_sid) {
                if (!fscontext_sid) {
-                       rc = may_context_mount_sb_relabel(context_sid, sbsec, tsec);
+                       rc = may_context_mount_sb_relabel(context_sid, sbsec,
+                                                         cred);
                        if (rc)
                                goto out;
                        sbsec->sid = context_sid;
                } else {
-                       rc = may_context_mount_inode_relabel(context_sid, sbsec, tsec);
+                       rc = may_context_mount_inode_relabel(context_sid, sbsec,
+                                                            cred);
                        if (rc)
                                goto out;
                }
@@ -712,7 +744,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
        }
 
        if (rootcontext_sid) {
-               rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec, tsec);
+               rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec,
+                                                    cred);
                if (rc)
                        goto out;
 
@@ -730,7 +763,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
 
                if (defcontext_sid != sbsec->def_sid) {
                        rc = may_context_mount_inode_relabel(defcontext_sid,
-                                                            sbsec, tsec);
+                                                            sbsec, cred);
                        if (rc)
                                goto out;
                }
@@ -1345,18 +1378,53 @@ static inline u32 signal_to_av(int sig)
        return perm;
 }
 
-/* Check permission betweeen a pair of tasks, e.g. signal checks,
-   fork check, ptrace check, etc. */
-static int task_has_perm(struct task_struct *tsk1,
-                        struct task_struct *tsk2,
+/*
+ * Check permission between a pair of credentials
+ * fork check, ptrace check, etc.
+ */
+static int cred_has_perm(const struct cred *actor,
+                        const struct cred *target,
+                        u32 perms)
+{
+       u32 asid = cred_sid(actor), tsid = cred_sid(target);
+
+       return avc_has_perm(asid, tsid, SECCLASS_PROCESS, perms, NULL);
+}
+
+/*
+ * Check permission between a pair of tasks, e.g. signal checks,
+ * fork check, ptrace check, etc.
+ * tsk1 is the actor and tsk2 is the target
+ * - this uses the default subjective creds of tsk1
+ */
+static int task_has_perm(const struct task_struct *tsk1,
+                        const struct task_struct *tsk2,
                         u32 perms)
 {
-       struct task_security_struct *tsec1, *tsec2;
+       const struct task_security_struct *__tsec1, *__tsec2;
+       u32 sid1, sid2;
 
-       tsec1 = tsk1->security;
-       tsec2 = tsk2->security;
-       return avc_has_perm(tsec1->sid, tsec2->sid,
-                           SECCLASS_PROCESS, perms, NULL);
+       rcu_read_lock();
+       __tsec1 = __task_cred(tsk1)->security;  sid1 = __tsec1->sid;
+       __tsec2 = __task_cred(tsk2)->security;  sid2 = __tsec2->sid;
+       rcu_read_unlock();
+       return avc_has_perm(sid1, sid2, SECCLASS_PROCESS, perms, NULL);
+}
+
+/*
+ * Check permission between current and another task, e.g. signal checks,
+ * fork check, ptrace check, etc.
+ * current is the actor and tsk2 is the target
+ * - this uses current's subjective creds
+ */
+static int current_has_perm(const struct task_struct *tsk,
+                           u32 perms)
+{
+       u32 sid, tsid;
+
+       sid = current_sid();
+       tsid = task_sid(tsk);
+       return avc_has_perm(sid, tsid, SECCLASS_PROCESS, perms, NULL);
 }
 
 #if CAP_LAST_CAP > 63
@@ -1365,14 +1433,14 @@ static int task_has_perm(struct task_struct *tsk1,
 
 /* Check whether a task is allowed to use a capability. */
 static int task_has_capability(struct task_struct *tsk,
-                              int cap)
+                              int cap, int audit)
 {
-       struct task_security_struct *tsec;
        struct avc_audit_data ad;
+       struct av_decision avd;
        u16 sclass;
+       u32 sid = task_sid(tsk);
        u32 av = CAP_TO_MASK(cap);
-
-       tsec = tsk->security;
+       int rc;
 
        AVC_AUDIT_DATA_INIT(&ad, CAP);
        ad.tsk = tsk;
@@ -1390,37 +1458,39 @@ static int task_has_capability(struct task_struct *tsk,
                       "SELinux:  out of range capability %d\n", cap);
                BUG();
        }
-       return avc_has_perm(tsec->sid, tsec->sid, sclass, av, &ad);
+
+       rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd);
+       if (audit == SECURITY_CAP_AUDIT)
+               avc_audit(sid, sid, sclass, av, &avd, rc, &ad);
+       return rc;
 }
 
 /* Check whether a task is allowed to use a system operation. */
 static int task_has_system(struct task_struct *tsk,
                           u32 perms)
 {
-       struct task_security_struct *tsec;
+       u32 sid = task_sid(tsk);
 
-       tsec = tsk->security;
-
-       return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
+       return avc_has_perm(sid, SECINITSID_KERNEL,
                            SECCLASS_SYSTEM, perms, NULL);
 }
 
 /* Check whether a task has a particular permission to an inode.
    The 'adp' parameter is optional and allows other audit
    data to be passed (e.g. the dentry). */
-static int inode_has_perm(struct task_struct *tsk,
+static int inode_has_perm(const struct cred *cred,
                          struct inode *inode,
                          u32 perms,
                          struct avc_audit_data *adp)
 {
-       struct task_security_struct *tsec;
        struct inode_security_struct *isec;
        struct avc_audit_data ad;
+       u32 sid;
 
        if (unlikely(IS_PRIVATE(inode)))
                return 0;
 
-       tsec = tsk->security;
+       sid = cred_sid(cred);
        isec = inode->i_security;
 
        if (!adp) {
@@ -1429,23 +1499,24 @@ static int inode_has_perm(struct task_struct *tsk,
                ad.u.fs.inode = inode;
        }
 
-       return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp);
+       return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);
 }
 
 /* Same as inode_has_perm, but pass explicit audit data containing
    the dentry to help the auditing code to more easily generate the
    pathname if needed. */
-static inline int dentry_has_perm(struct task_struct *tsk,
+static inline int dentry_has_perm(const struct cred *cred,
                                  struct vfsmount *mnt,
                                  struct dentry *dentry,
                                  u32 av)
 {
        struct inode *inode = dentry->d_inode;
        struct avc_audit_data ad;
+
        AVC_AUDIT_DATA_INIT(&ad, FS);
        ad.u.fs.path.mnt = mnt;
        ad.u.fs.path.dentry = dentry;
-       return inode_has_perm(tsk, inode, av, &ad);
+       return inode_has_perm(cred, inode, av, &ad);
 }
 
 /* Check whether a task can use an open file descriptor to
@@ -1456,33 +1527,35 @@ static inline int dentry_has_perm(struct task_struct *tsk,
    has the same SID as the process.  If av is zero, then
    access to the file is not checked, e.g. for cases
    where only the descriptor is affected like seek. */
-static int file_has_perm(struct task_struct *tsk,
-                               struct file *file,
-                               u32 av)
+static int file_has_perm(const struct cred *cred,
+                        struct file *file,
+                        u32 av)
 {
-       struct task_security_struct *tsec = tsk->security;
        struct file_security_struct *fsec = file->f_security;
        struct inode *inode = file->f_path.dentry->d_inode;
        struct avc_audit_data ad;
+       u32 sid = cred_sid(cred);
        int rc;
 
        AVC_AUDIT_DATA_INIT(&ad, FS);
        ad.u.fs.path = file->f_path;
 
-       if (tsec->sid != fsec->sid) {
-               rc = avc_has_perm(tsec->sid, fsec->sid,
+       if (sid != fsec->sid) {
+               rc = avc_has_perm(sid, fsec->sid,
                                  SECCLASS_FD,
                                  FD__USE,
                                  &ad);
                if (rc)
-                       return rc;
+                       goto out;
        }
 
        /* av is zero if only checking access to the descriptor. */
+       rc = 0;
        if (av)
-               return inode_has_perm(tsk, inode, av, &ad);
+               rc = inode_has_perm(cred, inode, av, &ad);
 
-       return 0;
+out:
+       return rc;
 }
 
 /* Check whether a task can create a file. */
@@ -1490,36 +1563,36 @@ static int may_create(struct inode *dir,
                      struct dentry *dentry,
                      u16 tclass)
 {
-       struct task_security_struct *tsec;
+       const struct cred *cred = current_cred();
+       const struct task_security_struct *tsec = cred->security;
        struct inode_security_struct *dsec;
        struct superblock_security_struct *sbsec;
-       u32 newsid;
+       u32 sid, newsid;
        struct avc_audit_data ad;
        int rc;
 
-       tsec = current->security;
        dsec = dir->i_security;
        sbsec = dir->i_sb->s_security;
 
+       sid = tsec->sid;
+       newsid = tsec->create_sid;
+
        AVC_AUDIT_DATA_INIT(&ad, FS);
        ad.u.fs.path.dentry = dentry;
 
-       rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
+       rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR,
                          DIR__ADD_NAME | DIR__SEARCH,
                          &ad);
        if (rc)
                return rc;
 
-       if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
-               newsid = tsec->create_sid;
-       } else {
-               rc = security_transition_sid(tsec->sid, dsec->sid, tclass,
-                                            &newsid);
+       if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+               rc = security_transition_sid(sid, dsec->sid, tclass, &newsid);
                if (rc)
                        return rc;
        }
 
-       rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad);
+       rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad);
        if (rc)
                return rc;
 
@@ -1532,11 +1605,9 @@ static int may_create(struct inode *dir,
 static int may_create_key(u32 ksid,
                          struct task_struct *ctx)
 {
-       struct task_security_struct *tsec;
+       u32 sid = task_sid(ctx);
 
-       tsec = ctx->security;
-
-       return avc_has_perm(tsec->sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
+       return avc_has_perm(sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
 }
 
 #define MAY_LINK       0
@@ -1549,13 +1620,12 @@ static int may_link(struct inode *dir,
                    int kind)
 
 {
-       struct task_security_struct *tsec;
        struct inode_security_struct *dsec, *isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
        u32 av;
        int rc;
 
-       tsec = current->security;
        dsec = dir->i_security;
        isec = dentry->d_inode->i_security;
 
@@ -1564,7 +1634,7 @@ static int may_link(struct inode *dir,
 
        av = DIR__SEARCH;
        av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
-       rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad);
+       rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, av, &ad);
        if (rc)
                return rc;
 
@@ -1584,7 +1654,7 @@ static int may_link(struct inode *dir,
                return 0;
        }
 
-       rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad);
+       rc = avc_has_perm(sid, isec->sid, isec->sclass, av, &ad);
        return rc;
 }
 
@@ -1593,14 +1663,13 @@ static inline int may_rename(struct inode *old_dir,
                             struct inode *new_dir,
                             struct dentry *new_dentry)
 {
-       struct task_security_struct *tsec;
        struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
        u32 av;
        int old_is_dir, new_is_dir;
        int rc;
 
-       tsec = current->security;
        old_dsec = old_dir->i_security;
        old_isec = old_dentry->d_inode->i_security;
        old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
@@ -1609,16 +1678,16 @@ static inline int may_rename(struct inode *old_dir,
        AVC_AUDIT_DATA_INIT(&ad, FS);
 
        ad.u.fs.path.dentry = old_dentry;
-       rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
+       rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR,
                          DIR__REMOVE_NAME | DIR__SEARCH, &ad);
        if (rc)
                return rc;
-       rc = avc_has_perm(tsec->sid, old_isec->sid,
+       rc = avc_has_perm(sid, old_isec->sid,
                          old_isec->sclass, FILE__RENAME, &ad);
        if (rc)
                return rc;
        if (old_is_dir && new_dir != old_dir) {
-               rc = avc_has_perm(tsec->sid, old_isec->sid,
+               rc = avc_has_perm(sid, old_isec->sid,
                                  old_isec->sclass, DIR__REPARENT, &ad);
                if (rc)
                        return rc;
@@ -1628,13 +1697,13 @@ static inline int may_rename(struct inode *old_dir,
        av = DIR__ADD_NAME | DIR__SEARCH;
        if (new_dentry->d_inode)
                av |= DIR__REMOVE_NAME;
-       rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
+       rc = avc_has_perm(sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
        if (rc)
                return rc;
        if (new_dentry->d_inode) {
                new_isec = new_dentry->d_inode->i_security;
                new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
-               rc = avc_has_perm(tsec->sid, new_isec->sid,
+               rc = avc_has_perm(sid, new_isec->sid,
                                  new_isec->sclass,
                                  (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);
                if (rc)
@@ -1645,18 +1714,16 @@ static inline int may_rename(struct inode *old_dir,
 }
 
 /* Check whether a task can perform a filesystem operation. */
-static int superblock_has_perm(struct task_struct *tsk,
+static int superblock_has_perm(const struct cred *cred,
                               struct super_block *sb,
                               u32 perms,
                               struct avc_audit_data *ad)
 {
-       struct task_security_struct *tsec;
        struct superblock_security_struct *sbsec;
+       u32 sid = cred_sid(cred);
 
-       tsec = tsk->security;
        sbsec = sb->s_security;
-       return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
-                           perms, ad);
+       return avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad);
 }
 
 /* Convert a Linux mode and permission mask to an access vector. */
@@ -1687,15 +1754,39 @@ static inline u32 file_mask_to_av(int mode, int mask)
        return av;
 }
 
+/* Convert a Linux file to an access vector. */
+static inline u32 file_to_av(struct file *file)
+{
+       u32 av = 0;
+
+       if (file->f_mode & FMODE_READ)
+               av |= FILE__READ;
+       if (file->f_mode & FMODE_WRITE) {
+               if (file->f_flags & O_APPEND)
+                       av |= FILE__APPEND;
+               else
+                       av |= FILE__WRITE;
+       }
+       if (!av) {
+               /*
+                * Special file opened with flags 3 for ioctl-only use.
+                */
+               av = FILE__IOCTL;
+       }
+
+       return av;
+}
+
 /*
- * Convert a file mask to an access vector and include the correct open
+ * Convert a file to an access vector and include the correct open
  * open permission.
  */
-static inline u32 open_file_mask_to_av(int mode, int mask)
+static inline u32 open_file_to_av(struct file *file)
 {
-       u32 av = file_mask_to_av(mode, mask);
+       u32 av = file_to_av(file);
 
        if (selinux_policycap_openperm) {
+               mode_t mode = file->f_path.dentry->d_inode->i_mode;
                /*
                 * lnk files and socks do not really have an 'open'
                 */
@@ -1711,34 +1802,11 @@ static inline u32 open_file_mask_to_av(int mode, int mask)
                        av |= DIR__OPEN;
                else
                        printk(KERN_ERR "SELinux: WARNING: inside %s with "
-                               "unknown mode:%x\n", __func__, mode);
+                               "unknown mode:%o\n", __func__, mode);
        }
        return av;
 }
 
-/* Convert a Linux file to an access vector. */
-static inline u32 file_to_av(struct file *file)
-{
-       u32 av = 0;
-
-       if (file->f_mode & FMODE_READ)
-               av |= FILE__READ;
-       if (file->f_mode & FMODE_WRITE) {
-               if (file->f_flags & O_APPEND)
-                       av |= FILE__APPEND;
-               else
-                       av |= FILE__WRITE;
-       }
-       if (!av) {
-               /*
-                * Special file opened with flags 3 for ioctl-only use.
-                */
-               av = FILE__IOCTL;
-       }
-
-       return av;
-}
-
 /* Hook functions begin here. */
 
 static int selinux_ptrace_may_access(struct task_struct *child,
@@ -1751,13 +1819,12 @@ static int selinux_ptrace_may_access(struct task_struct *child,
                return rc;
 
        if (mode == PTRACE_MODE_READ) {
-               struct task_security_struct *tsec = current->security;
-               struct task_security_struct *csec = child->security;
-               return avc_has_perm(tsec->sid, csec->sid,
-                                   SECCLASS_FILE, FILE__READ, NULL);
+               u32 sid = current_sid();
+               u32 csid = task_sid(child);
+               return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL);
        }
 
-       return task_has_perm(current, child, PROCESS__PTRACE);
+       return current_has_perm(child, PROCESS__PTRACE);
 }
 
 static int selinux_ptrace_traceme(struct task_struct *parent)
@@ -1776,40 +1843,37 @@ static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
 {
        int error;
 
-       error = task_has_perm(current, target, PROCESS__GETCAP);
+       error = current_has_perm(target, PROCESS__GETCAP);
        if (error)
                return error;
 
        return secondary_ops->capget(target, effective, inheritable, permitted);
 }
 
-static int selinux_capset_check(struct task_struct *target, kernel_cap_t *effective,
-                               kernel_cap_t *inheritable, kernel_cap_t *permitted)
+static int selinux_capset(struct cred *new, const struct cred *old,
+                         const kernel_cap_t *effective,
+                         const kernel_cap_t *inheritable,
+                         const kernel_cap_t *permitted)
 {
        int error;
 
-       error = secondary_ops->capset_check(target, effective, inheritable, permitted);
+       error = secondary_ops->capset(new, old,
+                                     effective, inheritable, permitted);
        if (error)
                return error;
 
-       return task_has_perm(current, target, PROCESS__SETCAP);
+       return cred_has_perm(old, new, PROCESS__SETCAP);
 }
 
-static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective,
-                              kernel_cap_t *inheritable, kernel_cap_t *permitted)
-{
-       secondary_ops->capset_set(target, effective, inheritable, permitted);
-}
-
-static int selinux_capable(struct task_struct *tsk, int cap)
+static int selinux_capable(struct task_struct *tsk, int cap, int audit)
 {
        int rc;
 
-       rc = secondary_ops->capable(tsk, cap);
+       rc = secondary_ops->capable(tsk, cap, audit);
        if (rc)
                return rc;
 
-       return task_has_capability(tsk, cap);
+       return task_has_capability(tsk, cap, audit);
 }
 
 static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid)
@@ -1857,15 +1921,14 @@ static int selinux_sysctl(ctl_table *table, int op)
 {
        int error = 0;
        u32 av;
-       struct task_security_struct *tsec;
-       u32 tsid;
+       u32 tsid, sid;
        int rc;
 
        rc = secondary_ops->sysctl(table, op);
        if (rc)
                return rc;
 
-       tsec = current->security;
+       sid = current_sid();
 
        rc = selinux_sysctl_get_sid(table, (op == 0001) ?
                                    SECCLASS_DIR : SECCLASS_FILE, &tsid);
@@ -1877,7 +1940,7 @@ static int selinux_sysctl(ctl_table *table, int op)
        /* The op values are "defined" in sysctl.c, thereby creating
         * a bad coupling between this module and sysctl.c */
        if (op == 001) {
-               error = avc_has_perm(tsec->sid, tsid,
+               error = avc_has_perm(sid, tsid,
                                     SECCLASS_DIR, DIR__SEARCH, NULL);
        } else {
                av = 0;
@@ -1886,7 +1949,7 @@ static int selinux_sysctl(ctl_table *table, int op)
                if (op & 002)
                        av |= FILE__WRITE;
                if (av)
-                       error = avc_has_perm(tsec->sid, tsid,
+                       error = avc_has_perm(sid, tsid,
                                             SECCLASS_FILE, av, NULL);
        }
 
@@ -1895,6 +1958,7 @@ static int selinux_sysctl(ctl_table *table, int op)
 
 static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
 {
+       const struct cred *cred = current_cred();
        int rc = 0;
 
        if (!sb)
@@ -1906,14 +1970,12 @@ static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
        case Q_QUOTAOFF:
        case Q_SETINFO:
        case Q_SETQUOTA:
-               rc = superblock_has_perm(current, sb, FILESYSTEM__QUOTAMOD,
-                                        NULL);
+               rc = superblock_has_perm(cred, sb, FILESYSTEM__QUOTAMOD, NULL);
                break;
        case Q_GETFMT:
        case Q_GETINFO:
        case Q_GETQUOTA:
-               rc = superblock_has_perm(current, sb, FILESYSTEM__QUOTAGET,
-                                        NULL);
+               rc = superblock_has_perm(cred, sb, FILESYSTEM__QUOTAGET, NULL);
                break;
        default:
                rc = 0;  /* let the kernel handle invalid cmds */
@@ -1924,7 +1986,9 @@ static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
 
 static int selinux_quota_on(struct dentry *dentry)
 {
-       return dentry_has_perm(current, NULL, dentry, FILE__QUOTAON);
+       const struct cred *cred = current_cred();
+
+       return dentry_has_perm(cred, NULL, dentry, FILE__QUOTAON);
 }
 
 static int selinux_syslog(int type)
@@ -1972,16 +2036,8 @@ static int selinux_syslog(int type)
 static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
 {
        int rc, cap_sys_admin = 0;
-       struct task_security_struct *tsec = current->security;
-
-       rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
-       if (rc == 0)
-               rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
-                                         SECCLASS_CAPABILITY,
-                                         CAP_TO_MASK(CAP_SYS_ADMIN),
-                                         0,
-                                         NULL);
 
+       rc = selinux_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT);
        if (rc == 0)
                cap_sys_admin = 1;
 
@@ -1990,59 +2046,45 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
 
 /* binprm security operations */
 
-static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
-{
-       struct bprm_security_struct *bsec;
-
-       bsec = kzalloc(sizeof(struct bprm_security_struct), GFP_KERNEL);
-       if (!bsec)
-               return -ENOMEM;
-
-       bsec->sid = SECINITSID_UNLABELED;
-       bsec->set = 0;
-
-       bprm->security = bsec;
-       return 0;
-}
-
-static int selinux_bprm_set_security(struct linux_binprm *bprm)
+static int selinux_bprm_set_creds(struct linux_binprm *bprm)
 {
-       struct task_security_struct *tsec;
-       struct inode *inode = bprm->file->f_path.dentry->d_inode;
+       const struct task_security_struct *old_tsec;
+       struct task_security_struct *new_tsec;
        struct inode_security_struct *isec;
-       struct bprm_security_struct *bsec;
-       u32 newsid;
        struct avc_audit_data ad;
+       struct inode *inode = bprm->file->f_path.dentry->d_inode;
        int rc;
 
-       rc = secondary_ops->bprm_set_security(bprm);
+       rc = secondary_ops->bprm_set_creds(bprm);
        if (rc)
                return rc;
 
-       bsec = bprm->security;
-
-       if (bsec->set)
+       /* SELinux context only depends on initial program or script and not
+        * the script interpreter */
+       if (bprm->cred_prepared)
                return 0;
 
-       tsec = current->security;
+       old_tsec = current_security();
+       new_tsec = bprm->cred->security;
        isec = inode->i_security;
 
        /* Default to the current task SID. */
-       bsec->sid = tsec->sid;
+       new_tsec->sid = old_tsec->sid;
+       new_tsec->osid = old_tsec->sid;
 
        /* Reset fs, key, and sock SIDs on execve. */
-       tsec->create_sid = 0;
-       tsec->keycreate_sid = 0;
-       tsec->sockcreate_sid = 0;
+       new_tsec->create_sid = 0;
+       new_tsec->keycreate_sid = 0;
+       new_tsec->sockcreate_sid = 0;
 
-       if (tsec->exec_sid) {
-               newsid = tsec->exec_sid;
+       if (old_tsec->exec_sid) {
+               new_tsec->sid = old_tsec->exec_sid;
                /* Reset exec SID on execve. */
-               tsec->exec_sid = 0;
+               new_tsec->exec_sid = 0;
        } else {
                /* Check for a default transition on this program. */
-               rc = security_transition_sid(tsec->sid, isec->sid,
-                                            SECCLASS_PROCESS, &newsid);
+               rc = security_transition_sid(old_tsec->sid, isec->sid,
+                                            SECCLASS_PROCESS, &new_tsec->sid);
                if (rc)
                        return rc;
        }
@@ -2051,33 +2093,63 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
        ad.u.fs.path = bprm->file->f_path;
 
        if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
-               newsid = tsec->sid;
+               new_tsec->sid = old_tsec->sid;
 
-       if (tsec->sid == newsid) {
-               rc = avc_has_perm(tsec->sid, isec->sid,
+       if (new_tsec->sid == old_tsec->sid) {
+               rc = avc_has_perm(old_tsec->sid, isec->sid,
                                  SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
                if (rc)
                        return rc;
        } else {
                /* Check permissions for the transition. */
-               rc = avc_has_perm(tsec->sid, newsid,
+               rc = avc_has_perm(old_tsec->sid, new_tsec->sid,
                                  SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
                if (rc)
                        return rc;
 
-               rc = avc_has_perm(newsid, isec->sid,
+               rc = avc_has_perm(new_tsec->sid, isec->sid,
                                  SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
                if (rc)
                        return rc;
 
-               /* Clear any possibly unsafe personality bits on exec: */
-               current->personality &= ~PER_CLEAR_ON_SETID;
+               /* Check for shared state */
+               if (bprm->unsafe & LSM_UNSAFE_SHARE) {
+                       rc = avc_has_perm(old_tsec->sid, new_tsec->sid,
+                                         SECCLASS_PROCESS, PROCESS__SHARE,
+                                         NULL);
+                       if (rc)
+                               return -EPERM;
+               }
+
+               /* Make sure that anyone attempting to ptrace over a task that
+                * changes its SID has the appropriate permit */
+               if (bprm->unsafe &
+                   (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
+                       struct task_struct *tracer;
+                       struct task_security_struct *sec;
+                       u32 ptsid = 0;
 
-               /* Set the security field to the new SID. */
-               bsec->sid = newsid;
+                       rcu_read_lock();
+                       tracer = tracehook_tracer_task(current);
+                       if (likely(tracer != NULL)) {
+                               sec = __task_cred(tracer)->security;
+                               ptsid = sec->sid;
+                       }
+                       rcu_read_unlock();
+
+                       if (ptsid != 0) {
+                               rc = avc_has_perm(ptsid, new_tsec->sid,
+                                                 SECCLASS_PROCESS,
+                                                 PROCESS__PTRACE, NULL);
+                               if (rc)
+                                       return -EPERM;
+                       }
+               }
+
+               /* Clear any possibly unsafe personality bits on exec: */
+               bprm->per_clear |= PER_CLEAR_ON_SETID;
        }
 
-       bsec->set = 1;
        return 0;
 }
 
@@ -2086,35 +2158,34 @@ static int selinux_bprm_check_security(struct linux_binprm *bprm)
        return secondary_ops->bprm_check_security(bprm);
 }
 
-
 static int selinux_bprm_secureexec(struct linux_binprm *bprm)
 {
-       struct task_security_struct *tsec = current->security;
+       const struct cred *cred = current_cred();
+       const struct task_security_struct *tsec = cred->security;
+       u32 sid, osid;
        int atsecure = 0;
 
-       if (tsec->osid != tsec->sid) {
+       sid = tsec->sid;
+       osid = tsec->osid;
+
+       if (osid != sid) {
                /* Enable secure mode for SIDs transitions unless
                   the noatsecure permission is granted between
                   the two SIDs, i.e. ahp returns 0. */
-               atsecure = avc_has_perm(tsec->osid, tsec->sid,
-                                        SECCLASS_PROCESS,
-                                        PROCESS__NOATSECURE, NULL);
+               atsecure = avc_has_perm(osid, sid,
+                                       SECCLASS_PROCESS,
+                                       PROCESS__NOATSECURE, NULL);
        }
 
        return (atsecure || secondary_ops->bprm_secureexec(bprm));
 }
 
-static void selinux_bprm_free_security(struct linux_binprm *bprm)
-{
-       kfree(bprm->security);
-       bprm->security = NULL;
-}
-
 extern struct vfsmount *selinuxfs_mount;
 extern struct dentry *selinux_null;
 
 /* Derived from fs/exec.c:flush_old_files. */
-static inline void flush_unauthorized_files(struct files_struct *files)
+static inline void flush_unauthorized_files(const struct cred *cred,
+                                           struct files_struct *files)
 {
        struct avc_audit_data ad;
        struct file *file, *devnull = NULL;
@@ -2136,7 +2207,7 @@ static inline void flush_unauthorized_files(struct files_struct *files)
                           interested in the inode-based check here. */
                        file = list_first_entry(&tty->tty_files, struct file, f_u.fu_list);
                        inode = file->f_path.dentry->d_inode;
-                       if (inode_has_perm(current, inode,
+                       if (inode_has_perm(cred, inode,
                                           FILE__READ | FILE__WRITE, NULL)) {
                                drop_tty = 1;
                        }
@@ -2171,7 +2242,7 @@ static inline void flush_unauthorized_files(struct files_struct *files)
                                file = fget(i);
                                if (!file)
                                        continue;
-                               if (file_has_perm(current,
+                               if (file_has_perm(cred,
                                                  file,
                                                  file_to_av(file))) {
                                        sys_close(i);
@@ -2185,7 +2256,10 @@ static inline void flush_unauthorized_files(struct files_struct *files)
                                        if (devnull) {
                                                get_file(devnull);
                                        } else {
-                                               devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR);
+                                               devnull = dentry_open(
+                                                       dget(selinux_null),
+                                                       mntget(selinuxfs_mount),
+                                                       O_RDWR, cred);
                                                if (IS_ERR(devnull)) {
                                                        devnull = NULL;
                                                        put_unused_fd(fd);
@@ -2204,94 +2278,78 @@ static inline void flush_unauthorized_files(struct files_struct *files)
        spin_unlock(&files->file_lock);
 }
 
-static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
+/*
+ * Prepare a process for imminent new credential changes due to exec
+ */
+static void selinux_bprm_committing_creds(struct linux_binprm *bprm)
 {
-       struct task_security_struct *tsec;
-       struct bprm_security_struct *bsec;
-       u32 sid;
-       int rc;
-
-       secondary_ops->bprm_apply_creds(bprm, unsafe);
-
-       tsec = current->security;
+       struct task_security_struct *new_tsec;
+       struct rlimit *rlim, *initrlim;
+       int rc, i;
 
-       bsec = bprm->security;
-       sid = bsec->sid;
+       secondary_ops->bprm_committing_creds(bprm);
 
-       tsec->osid = tsec->sid;
-       bsec->unsafe = 0;
-       if (tsec->sid != sid) {
-               /* Check for shared state.  If not ok, leave SID
-                  unchanged and kill. */
-               if (unsafe & LSM_UNSAFE_SHARE) {
-                       rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
-                                       PROCESS__SHARE, NULL);
-                       if (rc) {
-                               bsec->unsafe = 1;
-                               return;
-                       }
-               }
+       new_tsec = bprm->cred->security;
+       if (new_tsec->sid == new_tsec->osid)
+               return;
 
-               /* Check for ptracing, and update the task SID if ok.
-                  Otherwise, leave SID unchanged and kill. */
-               if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
-                       struct task_struct *tracer;
-                       struct task_security_struct *sec;
-                       u32 ptsid = 0;
+       /* Close files for which the new task SID is not authorized. */
+       flush_unauthorized_files(bprm->cred, current->files);
 
-                       rcu_read_lock();
-                       tracer = tracehook_tracer_task(current);
-                       if (likely(tracer != NULL)) {
-                               sec = tracer->security;
-                               ptsid = sec->sid;
-                       }
-                       rcu_read_unlock();
+       /* Always clear parent death signal on SID transitions. */
+       current->pdeath_signal = 0;
 
-                       if (ptsid != 0) {
-                               rc = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
-                                                 PROCESS__PTRACE, NULL);
-                               if (rc) {
-                                       bsec->unsafe = 1;
-                                       return;
-                               }
-                       }
+       /* Check whether the new SID can inherit resource limits from the old
+        * SID.  If not, reset all soft limits to the lower of the current
+        * task's hard limit and the init task's soft limit.
+        *
+        * Note that the setting of hard limits (even to lower them) can be
+        * controlled by the setrlimit check.  The inclusion of the init task's
+        * soft limit into the computation is to avoid resetting soft limits
+        * higher than the default soft limit for cases where the default is
+        * lower than the hard limit, e.g. RLIMIT_CORE or RLIMIT_STACK.
+        */
+       rc = avc_has_perm(new_tsec->osid, new_tsec->sid, SECCLASS_PROCESS,
+                         PROCESS__RLIMITINH, NULL);
+       if (rc) {
+               for (i = 0; i < RLIM_NLIMITS; i++) {
+                       rlim = current->signal->rlim + i;
+                       initrlim = init_task.signal->rlim + i;
+                       rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur);
                }
-               tsec->sid = sid;
+               update_rlimit_cpu(rlim->rlim_cur);
        }
 }
 
 /*
- * called after apply_creds without the task lock held
+ * Clean up the process immediately after the installation of new credentials
+ * due to exec
  */
-static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
+static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
 {
-       struct task_security_struct *tsec;
-       struct rlimit *rlim, *initrlim;
+       const struct task_security_struct *tsec = current_security();
        struct itimerval itimer;
-       struct bprm_security_struct *bsec;
+       struct sighand_struct *psig;
+       u32 osid, sid;
        int rc, i;
+       unsigned long flags;
 
-       tsec = current->security;
-       bsec = bprm->security;
+       secondary_ops->bprm_committed_creds(bprm);
 
-       if (bsec->unsafe) {
-               force_sig_specific(SIGKILL, current);
-               return;
-       }
-       if (tsec->osid == tsec->sid)
+       osid = tsec->osid;
+       sid = tsec->sid;
+
+       if (sid == osid)
                return;
 
-       /* Close files for which the new task SID is not authorized. */
-       flush_unauthorized_files(current->files);
-
-       /* Check whether the new SID can inherit signal state
-          from the old SID.  If not, clear itimers to avoid
-          subsequent signal generation and flush and unblock
-          signals. This must occur _after_ the task SID has
-         been updated so that any kill done after the flush
-         will be checked against the new SID. */
-       rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
-                         PROCESS__SIGINH, NULL);
+       /* Check whether the new SID can inherit signal state from the old SID.
+        * If not, clear itimers to avoid subsequent signal generation and
+        * flush and unblock signals.
+        *
+        * This must occur _after_ the task SID has been updated so that any
+        * kill done after the flush will be checked against the new SID.
+        */
+       rc = avc_has_perm(osid, sid, SECCLASS_PROCESS, PROCESS__SIGINH, NULL);
        if (rc) {
                memset(&itimer, 0, sizeof itimer);
                for (i = 0; i < 3; i++)
@@ -2304,33 +2362,14 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
                spin_unlock_irq(&current->sighand->siglock);
        }
 
-       /* Always clear parent death signal on SID transitions. */
-       current->pdeath_signal = 0;
-
-       /* Check whether the new SID can inherit resource limits
-          from the old SID.  If not, reset all soft limits to
-          the lower of the current task's hard limit and the init
-          task's soft limit.  Note that the setting of hard limits
-          (even to lower them) can be controlled by the setrlimit
-          check. The inclusion of the init task's soft limit into
-          the computation is to avoid resetting soft limits higher
-          than the default soft limit for cases where the default
-          is lower than the hard limit, e.g. RLIMIT_CORE or
-          RLIMIT_STACK.*/
-       rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
-                         PROCESS__RLIMITINH, NULL);
-       if (rc) {
-               for (i = 0; i < RLIM_NLIMITS; i++) {
-                       rlim = current->signal->rlim + i;
-                       initrlim = init_task.signal->rlim+i;
-                       rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur);
-               }
-               update_rlimit_cpu(rlim->rlim_cur);
-       }
-
-       /* Wake up the parent if it is waiting so that it can
-          recheck wait permission to the new task SID. */
+       /* Wake up the parent if it is waiting so that it can recheck
+        * wait permission to the new task SID. */
+       read_lock_irq(&tasklist_lock);
+       psig = current->parent->sighand;
+       spin_lock_irqsave(&psig->siglock, flags);
        wake_up_interruptible(&current->parent->signal->wait_chldexit);
+       spin_unlock_irqrestore(&psig->siglock, flags);
+       read_unlock_irq(&tasklist_lock);
 }
 
 /* superblock security operations */
@@ -2435,8 +2474,9 @@ out:
        return rc;
 }
 
-static int selinux_sb_kern_mount(struct super_block *sb, void *data)
+static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
 {
+       const struct cred *cred = current_cred();
        struct avc_audit_data ad;
        int rc;
 
@@ -2444,18 +2484,23 @@ static int selinux_sb_kern_mount(struct super_block *sb, void *data)
        if (rc)
                return rc;
 
+       /* Allow all mounts performed by the kernel */
+       if (flags & MS_KERNMOUNT)
+               return 0;
+
        AVC_AUDIT_DATA_INIT(&ad, FS);
        ad.u.fs.path.dentry = sb->s_root;
-       return superblock_has_perm(current, sb, FILESYSTEM__MOUNT, &ad);
+       return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad);
 }
 
 static int selinux_sb_statfs(struct dentry *dentry)
 {
+       const struct cred *cred = current_cred();
        struct avc_audit_data ad;
 
        AVC_AUDIT_DATA_INIT(&ad, FS);
        ad.u.fs.path.dentry = dentry->d_sb->s_root;
-       return superblock_has_perm(current, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
+       return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
 }
 
 static int selinux_mount(char *dev_name,
@@ -2464,6 +2509,7 @@ static int selinux_mount(char *dev_name,
                         unsigned long flags,
                         void *data)
 {
+       const struct cred *cred = current_cred();
        int rc;
 
        rc = secondary_ops->sb_mount(dev_name, path, type, flags, data);
@@ -2471,22 +2517,23 @@ static int selinux_mount(char *dev_name,
                return rc;
 
        if (flags & MS_REMOUNT)
-               return superblock_has_perm(current, path->mnt->mnt_sb,
+               return superblock_has_perm(cred, path->mnt->mnt_sb,
                                           FILESYSTEM__REMOUNT, NULL);
        else
-               return dentry_has_perm(current, path->mnt, path->dentry,
+               return dentry_has_perm(cred, path->mnt, path->dentry,
                                       FILE__MOUNTON);
 }
 
 static int selinux_umount(struct vfsmount *mnt, int flags)
 {
+       const struct cred *cred = current_cred();
        int rc;
 
        rc = secondary_ops->sb_umount(mnt, flags);
        if (rc)
                return rc;
 
-       return superblock_has_perm(current, mnt->mnt_sb,
+       return superblock_has_perm(cred, mnt->mnt_sb,
                                   FILESYSTEM__UNMOUNT, NULL);
 }
 
@@ -2506,21 +2553,22 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
                                       char **name, void **value,
                                       size_t *len)
 {
-       struct task_security_struct *tsec;
+       const struct cred *cred = current_cred();
+       const struct task_security_struct *tsec = cred->security;
        struct inode_security_struct *dsec;
        struct superblock_security_struct *sbsec;
-       u32 newsid, clen;
+       u32 sid, newsid, clen;
        int rc;
        char *namep = NULL, *context;
 
-       tsec = current->security;
        dsec = dir->i_security;
        sbsec = dir->i_sb->s_security;
 
-       if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
-               newsid = tsec->create_sid;
-       } else {
-               rc = security_transition_sid(tsec->sid, dsec->sid,
+       sid = tsec->sid;
+       newsid = tsec->create_sid;
+
+       if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+               rc = security_transition_sid(sid, dsec->sid,
                                             inode_mode_to_security_class(inode->i_mode),
                                             &newsid);
                if (rc) {
@@ -2623,21 +2671,25 @@ static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dent
 
 static int selinux_inode_readlink(struct dentry *dentry)
 {
-       return dentry_has_perm(current, NULL, dentry, FILE__READ);
+       const struct cred *cred = current_cred();
+
+       return dentry_has_perm(cred, NULL, dentry, FILE__READ);
 }
 
 static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
 {
+       const struct cred *cred = current_cred();
        int rc;
 
        rc = secondary_ops->inode_follow_link(dentry, nameidata);
        if (rc)
                return rc;
-       return dentry_has_perm(current, NULL, dentry, FILE__READ);
+       return dentry_has_perm(cred, NULL, dentry, FILE__READ);
 }
 
 static int selinux_inode_permission(struct inode *inode, int mask)
 {
+       const struct cred *cred = current_cred();
        int rc;
 
        rc = secondary_ops->inode_permission(inode, mask);
@@ -2649,12 +2701,13 @@ static int selinux_inode_permission(struct inode *inode, int mask)
                return 0;
        }
 
-       return inode_has_perm(current, inode,
-                              open_file_mask_to_av(inode->i_mode, mask), NULL);
+       return inode_has_perm(cred, inode,
+                             file_mask_to_av(inode->i_mode, mask), NULL);
 }
 
 static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
 {
+       const struct cred *cred = current_cred();
        int rc;
 
        rc = secondary_ops->inode_setattr(dentry, iattr);
@@ -2666,18 +2719,22 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
 
        if (iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
                               ATTR_ATIME_SET | ATTR_MTIME_SET))
-               return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
+               return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR);
 
-       return dentry_has_perm(current, NULL, dentry, FILE__WRITE);
+       return dentry_has_perm(cred, NULL, dentry, FILE__WRITE);
 }
 
 static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
 {
-       return dentry_has_perm(current, mnt, dentry, FILE__GETATTR);
+       const struct cred *cred = current_cred();
+
+       return dentry_has_perm(cred, mnt, dentry, FILE__GETATTR);
 }
 
 static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)
 {
+       const struct cred *cred = current_cred();
+
        if (!strncmp(name, XATTR_SECURITY_PREFIX,
                     sizeof XATTR_SECURITY_PREFIX - 1)) {
                if (!strcmp(name, XATTR_NAME_CAPS)) {
@@ -2692,18 +2749,17 @@ static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)
 
        /* Not an attribute we recognize, so just check the
           ordinary setattr permission. */
-       return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
+       return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR);
 }
 
 static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
                                  const void *value, size_t size, int flags)
 {
-       struct task_security_struct *tsec = current->security;
        struct inode *inode = dentry->d_inode;
        struct inode_security_struct *isec = inode->i_security;
        struct superblock_security_struct *sbsec;
        struct avc_audit_data ad;
-       u32 newsid;
+       u32 newsid, sid = current_sid();
        int rc = 0;
 
        if (strcmp(name, XATTR_NAME_SELINUX))
@@ -2719,7 +2775,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
        AVC_AUDIT_DATA_INIT(&ad, FS);
        ad.u.fs.path.dentry = dentry;
 
-       rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
+       rc = avc_has_perm(sid, isec->sid, isec->sclass,
                          FILE__RELABELFROM, &ad);
        if (rc)
                return rc;
@@ -2733,12 +2789,12 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
        if (rc)
                return rc;
 
-       rc = avc_has_perm(tsec->sid, newsid, isec->sclass,
+       rc = avc_has_perm(sid, newsid, isec->sclass,
                          FILE__RELABELTO, &ad);
        if (rc)
                return rc;
 
-       rc = security_validate_transition(isec->sid, newsid, tsec->sid,
+       rc = security_validate_transition(isec->sid, newsid, sid,
                                          isec->sclass);
        if (rc)
                return rc;
@@ -2778,12 +2834,16 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
 
 static int selinux_inode_getxattr(struct dentry *dentry, const char *name)
 {
-       return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
+       const struct cred *cred = current_cred();
+
+       return dentry_has_perm(cred, NULL, dentry, FILE__GETATTR);
 }
 
 static int selinux_inode_listxattr(struct dentry *dentry)
 {
-       return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
+       const struct cred *cred = current_cred();
+
+       return dentry_has_perm(cred, NULL, dentry, FILE__GETATTR);
 }
 
 static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
@@ -2806,7 +2866,6 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name
        u32 size;
        int error;
        char *context = NULL;
-       struct task_security_struct *tsec = current->security;
        struct inode_security_struct *isec = inode->i_security;
 
        if (strcmp(name, XATTR_SELINUX_SUFFIX))
@@ -2821,13 +2880,7 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name
         * and lack of permission just means that we fall back to the
         * in-core context value, not a denial.
         */
-       error = secondary_ops->capable(current, CAP_MAC_ADMIN);
-       if (!error)
-               error = avc_has_perm_noaudit(tsec->sid, tsec->sid,
-                                            SECCLASS_CAPABILITY2,
-                                            CAPABILITY2__MAC_ADMIN,
-                                            0,
-                                            NULL);
+       error = selinux_capable(current, CAP_MAC_ADMIN, SECURITY_CAP_NOAUDIT);
        if (!error)
                error = security_sid_to_context_force(isec->sid, &context,
                                                      &size);
@@ -2894,6 +2947,7 @@ static void selinux_inode_getsecid(const struct inode *inode, u32 *secid)
 
 static int selinux_revalidate_file_permission(struct file *file, int mask)
 {
+       const struct cred *cred = current_cred();
        int rc;
        struct inode *inode = file->f_path.dentry->d_inode;
 
@@ -2906,7 +2960,7 @@ static int selinux_revalidate_file_permission(struct file *file, int mask)
        if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE))
                mask |= MAY_APPEND;
 
-       rc = file_has_perm(current, file,
+       rc = file_has_perm(cred, file,
                           file_mask_to_av(inode->i_mode, mask));
        if (rc)
                return rc;
@@ -2917,16 +2971,16 @@ static int selinux_revalidate_file_permission(struct file *file, int mask)
 static int selinux_file_permission(struct file *file, int mask)
 {
        struct inode *inode = file->f_path.dentry->d_inode;
-       struct task_security_struct *tsec = current->security;
        struct file_security_struct *fsec = file->f_security;
        struct inode_security_struct *isec = inode->i_security;
+       u32 sid = current_sid();
 
        if (!mask) {
                /* No permission to check.  Existence test. */
                return 0;
        }
 
-       if (tsec->sid == fsec->sid && fsec->isid == isec->sid
+       if (sid == fsec->sid && fsec->isid == isec->sid
            && fsec->pseqno == avc_policy_seqno())
                return selinux_netlbl_inode_permission(inode, mask);
 
@@ -2946,6 +3000,7 @@ static void selinux_file_free_security(struct file *file)
 static int selinux_file_ioctl(struct file *file, unsigned int cmd,
                              unsigned long arg)
 {
+       const struct cred *cred = current_cred();
        u32 av = 0;
 
        if (_IOC_DIR(cmd) & _IOC_WRITE)
@@ -2955,11 +3010,14 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
        if (!av)
                av = FILE__IOCTL;
 
-       return file_has_perm(current, file, av);
+       return file_has_perm(cred, file, av);
 }
 
 static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
 {
+       const struct cred *cred = current_cred();
+       int rc = 0;
+
 #ifndef CONFIG_PPC32
        if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
                /*
@@ -2967,9 +3025,9 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared
                 * private file mapping that will also be writable.
                 * This has an additional check.
                 */
-               int rc = task_has_perm(current, current, PROCESS__EXECMEM);
+               rc = cred_has_perm(cred, cred, PROCESS__EXECMEM);
                if (rc)
-                       return rc;
+                       goto error;
        }
 #endif
 
@@ -2984,9 +3042,11 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared
                if (prot & PROT_EXEC)
                        av |= FILE__EXECUTE;
 
-               return file_has_perm(current, file, av);
+               return file_has_perm(cred, file, av);
        }
-       return 0;
+
+error:
+       return rc;
 }
 
 static int selinux_file_mmap(struct file *file, unsigned long reqprot,
@@ -2994,7 +3054,7 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot,
                             unsigned long addr, unsigned long addr_only)
 {
        int rc = 0;
-       u32 sid = ((struct task_security_struct *)(current->security))->sid;
+       u32 sid = current_sid();
 
        if (addr < mmap_min_addr)
                rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
@@ -3013,6 +3073,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
                                 unsigned long reqprot,
                                 unsigned long prot)
 {
+       const struct cred *cred = current_cred();
        int rc;
 
        rc = secondary_ops->file_mprotect(vma, reqprot, prot);
@@ -3027,12 +3088,11 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
                rc = 0;
                if (vma->vm_start >= vma->vm_mm->start_brk &&
                    vma->vm_end <= vma->vm_mm->brk) {
-                       rc = task_has_perm(current, current,
-                                          PROCESS__EXECHEAP);
+                       rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP);
                } else if (!vma->vm_file &&
                           vma->vm_start <= vma->vm_mm->start_stack &&
                           vma->vm_end >= vma->vm_mm->start_stack) {
-                       rc = task_has_perm(current, current, PROCESS__EXECSTACK);
+                       rc = current_has_perm(current, PROCESS__EXECSTACK);
                } else if (vma->vm_file && vma->anon_vma) {
                        /*
                         * We are making executable a file mapping that has
@@ -3041,8 +3101,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
                         * modified content.  This typically should only
                         * occur for text relocations.
                         */
-                       rc = file_has_perm(current, vma->vm_file,
-                                          FILE__EXECMOD);
+                       rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD);
                }
                if (rc)
                        return rc;
@@ -3054,12 +3113,15 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
 
 static int selinux_file_lock(struct file *file, unsigned int cmd)
 {
-       return file_has_perm(current, file, FILE__LOCK);
+       const struct cred *cred = current_cred();
+
+       return file_has_perm(cred, file, FILE__LOCK);
 }
 
 static int selinux_file_fcntl(struct file *file, unsigned int cmd,
                              unsigned long arg)
 {
+       const struct cred *cred = current_cred();
        int err = 0;
 
        switch (cmd) {
@@ -3070,7 +3132,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
                }
 
                if ((file->f_flags & O_APPEND) && !(arg & O_APPEND)) {
-                       err = file_has_perm(current, file, FILE__WRITE);
+                       err = file_has_perm(cred, file, FILE__WRITE);
                        break;
                }
                /* fall through */
@@ -3080,7 +3142,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
        case F_GETOWN:
        case F_GETSIG:
                /* Just check FD__USE permission */
-               err = file_has_perm(current, file, 0);
+               err = file_has_perm(cred, file, 0);
                break;
        case F_GETLK:
        case F_SETLK:
@@ -3094,7 +3156,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
                        err = -EINVAL;
                        break;
                }
-               err = file_has_perm(current, file, FILE__LOCK);
+               err = file_has_perm(cred, file, FILE__LOCK);
                break;
        }
 
@@ -3103,12 +3165,10 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
 
 static int selinux_file_set_fowner(struct file *file)
 {
-       struct task_security_struct *tsec;
        struct file_security_struct *fsec;
 
-       tsec = current->security;
        fsec = file->f_security;
-       fsec->fown_sid = tsec->sid;
+       fsec->fown_sid = current_sid();
 
        return 0;
 }
@@ -3117,14 +3177,13 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
                                       struct fown_struct *fown, int signum)
 {
        struct file *file;
+       u32 sid = current_sid();
        u32 perm;
-       struct task_security_struct *tsec;
        struct file_security_struct *fsec;
 
        /* struct fown_struct is never outside the context of a struct file */
        file = container_of(fown, struct file, f_owner);
 
-       tsec = tsk->security;
        fsec = file->f_security;
 
        if (!signum)
@@ -3132,20 +3191,23 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
        else
                perm = signal_to_av(signum);
 
-       return avc_has_perm(fsec->fown_sid, tsec->sid,
+       return avc_has_perm(fsec->fown_sid, sid,
                            SECCLASS_PROCESS, perm, NULL);
 }
 
 static int selinux_file_receive(struct file *file)
 {
-       return file_has_perm(current, file, file_to_av(file));
+       const struct cred *cred = current_cred();
+
+       return file_has_perm(cred, file, file_to_av(file));
 }
 
-static int selinux_dentry_open(struct file *file)
+static int selinux_dentry_open(struct file *file, const struct cred *cred)
 {
        struct file_security_struct *fsec;
        struct inode *inode;
        struct inode_security_struct *isec;
+
        inode = file->f_path.dentry->d_inode;
        fsec = file->f_security;
        isec = inode->i_security;
@@ -3166,7 +3228,7 @@ static int selinux_dentry_open(struct file *file)
         * new inode label or new policy.
         * This check is not redundant - do not remove.
         */
-       return inode_has_perm(current, inode, file_to_av(file), NULL);
+       return inode_has_perm(cred, inode, open_file_to_av(file), NULL);
 }
 
 /* task security operations */
@@ -3179,36 +3241,88 @@ static int selinux_task_create(unsigned long clone_flags)
        if (rc)
                return rc;
 
-       return task_has_perm(current, current, PROCESS__FORK);
+       return current_has_perm(current, PROCESS__FORK);
 }
 
-static int selinux_task_alloc_security(struct task_struct *tsk)
+/*
+ * detach and free the LSM part of a set of credentials
+ */
+static void selinux_cred_free(struct cred *cred)
 {
-       struct task_security_struct *tsec1, *tsec2;
-       int rc;
-
-       tsec1 = current->security;
+       struct task_security_struct *tsec = cred->security;
+       cred->security = NULL;
+       kfree(tsec);
+}
 
-       rc = task_alloc_security(tsk);
-       if (rc)
-               return rc;
-       tsec2 = tsk->security;
+/*
+ * prepare a new set of credentials for modification
+ */
+static int selinux_cred_prepare(struct cred *new, const struct cred *old,
+                               gfp_t gfp)
+{
+       const struct task_security_struct *old_tsec;
+       struct task_security_struct *tsec;
 
-       tsec2->osid = tsec1->osid;
-       tsec2->sid = tsec1->sid;
+       old_tsec = old->security;
 
-       /* Retain the exec, fs, key, and sock SIDs across fork */
-       tsec2->exec_sid = tsec1->exec_sid;
-       tsec2->create_sid = tsec1->create_sid;
-       tsec2->keycreate_sid = tsec1->keycreate_sid;
-       tsec2->sockcreate_sid = tsec1->sockcreate_sid;
+       tsec = kmemdup(old_tsec, sizeof(struct task_security_struct), gfp);
+       if (!tsec)
+               return -ENOMEM;
 
+       new->security = tsec;
        return 0;
 }
 
-static void selinux_task_free_security(struct task_struct *tsk)
+/*
+ * commit new credentials
+ */
+static void selinux_cred_commit(struct cred *new, const struct cred *old)
+{
+       secondary_ops->cred_commit(new, old);
+}
+
+/*
+ * set the security data for a kernel service
+ * - all the creation contexts are set to unlabelled
+ */
+static int selinux_kernel_act_as(struct cred *new, u32 secid)
+{
+       struct task_security_struct *tsec = new->security;
+       u32 sid = current_sid();
+       int ret;
+
+       ret = avc_has_perm(sid, secid,
+                          SECCLASS_KERNEL_SERVICE,
+                          KERNEL_SERVICE__USE_AS_OVERRIDE,
+                          NULL);
+       if (ret == 0) {
+               tsec->sid = secid;
+               tsec->create_sid = 0;
+               tsec->keycreate_sid = 0;
+               tsec->sockcreate_sid = 0;
+       }
+       return ret;
+}
+
+/*
+ * set the file creation context in a security record to the same as the
+ * objective context of the specified inode
+ */
+static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
 {
-       task_free_security(tsk);
+       struct inode_security_struct *isec = inode->i_security;
+       struct task_security_struct *tsec = new->security;
+       u32 sid = current_sid();
+       int ret;
+
+       ret = avc_has_perm(sid, isec->sid,
+                          SECCLASS_KERNEL_SERVICE,
+                          KERNEL_SERVICE__CREATE_FILES_AS,
+                          NULL);
+
+       if (ret == 0)
+               tsec->create_sid = isec->sid;
+       return 0;
 }
 
 static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
@@ -3222,9 +3336,10 @@ static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
        return 0;
 }
 
-static int selinux_task_post_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
+static int selinux_task_fix_setuid(struct cred *new, const struct cred *old,
+                                  int flags)
 {
-       return secondary_ops->task_post_setuid(id0, id1, id2, flags);
+       return secondary_ops->task_fix_setuid(new, old, flags);
 }
 
 static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
@@ -3235,23 +3350,22 @@ static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
 
 static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
 {
-       return task_has_perm(current, p, PROCESS__SETPGID);
+       return current_has_perm(p, PROCESS__SETPGID);
 }
 
 static int selinux_task_getpgid(struct task_struct *p)
 {
-       return task_has_perm(current, p, PROCESS__GETPGID);
+       return current_has_perm(p, PROCESS__GETPGID);
 }
 
 static int selinux_task_getsid(struct task_struct *p)
 {
-       return task_has_perm(current, p, PROCESS__GETSESSION);
+       return current_has_perm(p, PROCESS__GETSESSION);
 }
 
 static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
 {
-       struct task_security_struct *tsec = p->security;
-       *secid = tsec->sid;
+       *secid = task_sid(p);
 }
 
 static int selinux_task_setgroups(struct group_info *group_info)
@@ -3268,7 +3382,7 @@ static int selinux_task_setnice(struct task_struct *p, int nice)
        if (rc)
                return rc;
 
-       return task_has_perm(current, p, PROCESS__SETSCHED);
+       return current_has_perm(p, PROCESS__SETSCHED);
 }
 
 static int selinux_task_setioprio(struct task_struct *p, int ioprio)
@@ -3279,12 +3393,12 @@ static int selinux_task_setioprio(struct task_struct *p, int ioprio)
        if (rc)
                return rc;
 
-       return task_has_perm(current, p, PROCESS__SETSCHED);
+       return current_has_perm(p, PROCESS__SETSCHED);
 }
 
 static int selinux_task_getioprio(struct task_struct *p)
 {
-       return task_has_perm(current, p, PROCESS__GETSCHED);
+       return current_has_perm(p, PROCESS__GETSCHED);
 }
 
 static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
@@ -3299,9 +3413,9 @@ static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim
        /* Control the ability to change the hard limit (whether
           lowering or raising it), so that the hard limit can
           later be used as a safe reset point for the soft limit
-          upon context transitions. See selinux_bprm_apply_creds. */
+          upon context transitions.  See selinux_bprm_committing_creds. */
        if (old_rlim->rlim_max != new_rlim->rlim_max)
-               return task_has_perm(current, current, PROCESS__SETRLIMIT);
+               return current_has_perm(current, PROCESS__SETRLIMIT);
 
        return 0;
 }
@@ -3314,17 +3428,17 @@ static int selinux_task_setscheduler(struct task_struct *p, int policy, struct s
        if (rc)
                return rc;
 
-       return task_has_perm(current, p, PROCESS__SETSCHED);
+       return current_has_perm(p, PROCESS__SETSCHED);
 }
 
 static int selinux_task_getscheduler(struct task_struct *p)
 {
-       return task_has_perm(current, p, PROCESS__GETSCHED);
+       return current_has_perm(p, PROCESS__GETSCHED);
 }
 
 static int selinux_task_movememory(struct task_struct *p)
 {
-       return task_has_perm(current, p, PROCESS__SETSCHED);
+       return current_has_perm(p, PROCESS__SETSCHED);
 }
 
 static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
@@ -3332,7 +3446,6 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
 {
        u32 perm;
        int rc;
-       struct task_security_struct *tsec;
 
        rc = secondary_ops->task_kill(p, info, sig, secid);
        if (rc)
@@ -3342,11 +3455,11 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
                perm = PROCESS__SIGNULL; /* null signal; existence test */
        else
                perm = signal_to_av(sig);
-       tsec = p->security;
        if (secid)
-               rc = avc_has_perm(secid, tsec->sid, SECCLASS_PROCESS, perm, NULL);
+               rc = avc_has_perm(secid, task_sid(p),
+                                 SECCLASS_PROCESS, perm, NULL);
        else
-               rc = task_has_perm(current, p, perm);
+               rc = current_has_perm(p, perm);
        return rc;
 }
 
@@ -3354,13 +3467,12 @@ static int selinux_task_prctl(int option,
                              unsigned long arg2,
                              unsigned long arg3,
                              unsigned long arg4,
-                             unsigned long arg5,
-                             long *rc_p)
+                             unsigned long arg5)
 {
        /* The current prctl operations do not appear to require
           any SELinux controls since they merely observe or modify
           the state of the current process. */
-       return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5, rc_p);
+       return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5);
 }
 
 static int selinux_task_wait(struct task_struct *p)
@@ -3368,27 +3480,14 @@ static int selinux_task_wait(struct task_struct *p)
        return task_has_perm(p, current, PROCESS__SIGCHLD);
 }
 
-static void selinux_task_reparent_to_init(struct task_struct *p)
-{
-       struct task_security_struct *tsec;
-
-       secondary_ops->task_reparent_to_init(p);
-
-       tsec = p->security;
-       tsec->osid = tsec->sid;
-       tsec->sid = SECINITSID_KERNEL;
-       return;
-}
-
 static void selinux_task_to_inode(struct task_struct *p,
                                  struct inode *inode)
 {
-       struct task_security_struct *tsec = p->security;
        struct inode_security_struct *isec = inode->i_security;
+       u32 sid = task_sid(p);
 
-       isec->sid = tsec->sid;
+       isec->sid = sid;
        isec->initialized = 1;
-       return;
 }
 
 /* Returns error only if unable to parse addresses */
@@ -3627,19 +3726,19 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,
                           u32 perms)
 {
        struct inode_security_struct *isec;
-       struct task_security_struct *tsec;
        struct avc_audit_data ad;
+       u32 sid;
        int err = 0;
 
-       tsec = task->security;
        isec = SOCK_INODE(sock)->i_security;
 
        if (isec->sid == SECINITSID_KERNEL)
                goto out;
+       sid = task_sid(task);
 
        AVC_AUDIT_DATA_INIT(&ad, NET);
        ad.u.net.sk = sock->sk;
-       err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
+       err = avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);
 
 out:
        return err;
@@ -3648,18 +3747,20 @@ out:
 static int selinux_socket_create(int family, int type,
                                 int protocol, int kern)
 {
+       const struct cred *cred = current_cred();
+       const struct task_security_struct *tsec = cred->security;
+       u32 sid, newsid;
+       u16 secclass;
        int err = 0;
-       struct task_security_struct *tsec;
-       u32 newsid;
 
        if (kern)
                goto out;
 
-       tsec = current->security;
-       newsid = tsec->sockcreate_sid ? : tsec->sid;
-       err = avc_has_perm(tsec->sid, newsid,
-                          socket_type_to_security_class(family, type,
-                          protocol), SOCKET__CREATE, NULL);
+       sid = tsec->sid;
+       newsid = tsec->sockcreate_sid ?sid;
+
+       secclass = socket_type_to_security_class(family, type, protocol);
+       err = avc_has_perm(sid, newsid, secclass, SOCKET__CREATE, NULL);
 
 out:
        return err;
@@ -3668,18 +3769,26 @@ out:
 static int selinux_socket_post_create(struct socket *sock, int family,
                                      int type, int protocol, int kern)
 {
-       int err = 0;
+       const struct cred *cred = current_cred();
+       const struct task_security_struct *tsec = cred->security;
        struct inode_security_struct *isec;
-       struct task_security_struct *tsec;
        struct sk_security_struct *sksec;
-       u32 newsid;
+       u32 sid, newsid;
+       int err = 0;
+
+       sid = tsec->sid;
+       newsid = tsec->sockcreate_sid;
 
        isec = SOCK_INODE(sock)->i_security;
 
-       tsec = current->security;
-       newsid = tsec->sockcreate_sid ? : tsec->sid;
+       if (kern)
+               isec->sid = SECINITSID_KERNEL;
+       else if (newsid)
+               isec->sid = newsid;
+       else
+               isec->sid = sid;
+
        isec->sclass = socket_type_to_security_class(family, type, protocol);
-       isec->sid = kern ? SECINITSID_KERNEL : newsid;
        isec->initialized = 1;
 
        if (sock->sk) {
@@ -3714,7 +3823,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
        if (family == PF_INET || family == PF_INET6) {
                char *addrp;
                struct inode_security_struct *isec;
-               struct task_security_struct *tsec;
                struct avc_audit_data ad;
                struct sockaddr_in *addr4 = NULL;
                struct sockaddr_in6 *addr6 = NULL;
@@ -3722,7 +3830,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
                struct sock *sk = sock->sk;
                u32 sid, node_perm;
 
-               tsec = current->security;
                isec = SOCK_INODE(sock)->i_security;
 
                if (family == PF_INET) {
@@ -4387,7 +4494,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
                                  "SELinux:  unrecognized netlink message"
                                  " type=%hu for sclass=%hu\n",
                                  nlh->nlmsg_type, isec->sclass);
-                       if (!selinux_enforcing)
+                       if (!selinux_enforcing || security_get_allow_unknown())
                                err = 0;
                }
 
@@ -4763,15 +4870,16 @@ static int ipc_alloc_security(struct task_struct *task,
                              struct kern_ipc_perm *perm,
                              u16 sclass)
 {
-       struct task_security_struct *tsec = task->security;
        struct ipc_security_struct *isec;
+       u32 sid;
 
        isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
        if (!isec)
                return -ENOMEM;
 
+       sid = task_sid(task);
        isec->sclass = sclass;
-       isec->sid = tsec->sid;
+       isec->sid = sid;
        perm->security = isec;
 
        return 0;
@@ -4809,17 +4917,16 @@ static void msg_msg_free_security(struct msg_msg *msg)
 static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
                        u32 perms)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
 
-       tsec = current->security;
        isec = ipc_perms->security;
 
        AVC_AUDIT_DATA_INIT(&ad, IPC);
        ad.u.ipc_id = ipc_perms->key;
 
-       return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
+       return avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);
 }
 
 static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
@@ -4835,22 +4942,21 @@ static void selinux_msg_msg_free_security(struct msg_msg *msg)
 /* message queue security operations */
 static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
        int rc;
 
        rc = ipc_alloc_security(current, &msq->q_perm, SECCLASS_MSGQ);
        if (rc)
                return rc;
 
-       tsec = current->security;
        isec = msq->q_perm.security;
 
        AVC_AUDIT_DATA_INIT(&ad, IPC);
        ad.u.ipc_id = msq->q_perm.key;
 
-       rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+       rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
                          MSGQ__CREATE, &ad);
        if (rc) {
                ipc_free_security(&msq->q_perm);
@@ -4866,17 +4972,16 @@ static void selinux_msg_queue_free_security(struct msg_queue *msq)
 
 static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
 
-       tsec = current->security;
        isec = msq->q_perm.security;
 
        AVC_AUDIT_DATA_INIT(&ad, IPC);
        ad.u.ipc_id = msq->q_perm.key;
 
-       return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+       return avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
                            MSGQ__ASSOCIATE, &ad);
 }
 
@@ -4910,13 +5015,12 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
 
 static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct msg_security_struct *msec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
        int rc;
 
-       tsec = current->security;
        isec = msq->q_perm.security;
        msec = msg->security;
 
@@ -4928,9 +5032,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
                 * Compute new sid based on current process and
                 * message queue this message will be stored in
                 */
-               rc = security_transition_sid(tsec->sid,
-                                            isec->sid,
-                                            SECCLASS_MSG,
+               rc = security_transition_sid(sid, isec->sid, SECCLASS_MSG,
                                             &msec->sid);
                if (rc)
                        return rc;
@@ -4940,16 +5042,16 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
        ad.u.ipc_id = msq->q_perm.key;
 
        /* Can this process write to the queue? */
-       rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+       rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
                          MSGQ__WRITE, &ad);
        if (!rc)
                /* Can this process send the message */
-               rc = avc_has_perm(tsec->sid, msec->sid,
-                                 SECCLASS_MSG, MSG__SEND, &ad);
+               rc = avc_has_perm(sid, msec->sid, SECCLASS_MSG,
+                                 MSG__SEND, &ad);
        if (!rc)
                /* Can the message be put in the queue? */
-               rc = avc_has_perm(msec->sid, isec->sid,
-                                 SECCLASS_MSGQ, MSGQ__ENQUEUE, &ad);
+               rc = avc_has_perm(msec->sid, isec->sid, SECCLASS_MSGQ,
+                                 MSGQ__ENQUEUE, &ad);
 
        return rc;
 }
@@ -4958,23 +5060,22 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
                                    struct task_struct *target,
                                    long type, int mode)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct msg_security_struct *msec;
        struct avc_audit_data ad;
+       u32 sid = task_sid(target);
        int rc;
 
-       tsec = target->security;
        isec = msq->q_perm.security;
        msec = msg->security;
 
        AVC_AUDIT_DATA_INIT(&ad, IPC);
        ad.u.ipc_id = msq->q_perm.key;
 
-       rc = avc_has_perm(tsec->sid, isec->sid,
+       rc = avc_has_perm(sid, isec->sid,
                          SECCLASS_MSGQ, MSGQ__READ, &ad);
        if (!rc)
-               rc = avc_has_perm(tsec->sid, msec->sid,
+               rc = avc_has_perm(sid, msec->sid,
                                  SECCLASS_MSG, MSG__RECEIVE, &ad);
        return rc;
 }
@@ -4982,22 +5083,21 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
 /* Shared Memory security operations */
 static int selinux_shm_alloc_security(struct shmid_kernel *shp)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
        int rc;
 
        rc = ipc_alloc_security(current, &shp->shm_perm, SECCLASS_SHM);
        if (rc)
                return rc;
 
-       tsec = current->security;
        isec = shp->shm_perm.security;
 
        AVC_AUDIT_DATA_INIT(&ad, IPC);
        ad.u.ipc_id = shp->shm_perm.key;
 
-       rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
+       rc = avc_has_perm(sid, isec->sid, SECCLASS_SHM,
                          SHM__CREATE, &ad);
        if (rc) {
                ipc_free_security(&shp->shm_perm);
@@ -5013,17 +5113,16 @@ static void selinux_shm_free_security(struct shmid_kernel *shp)
 
 static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
 
-       tsec = current->security;
        isec = shp->shm_perm.security;
 
        AVC_AUDIT_DATA_INIT(&ad, IPC);
        ad.u.ipc_id = shp->shm_perm.key;
 
-       return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
+       return avc_has_perm(sid, isec->sid, SECCLASS_SHM,
                            SHM__ASSOCIATE, &ad);
 }
 
@@ -5081,22 +5180,21 @@ static int selinux_shm_shmat(struct shmid_kernel *shp,
 /* Semaphore security operations */
 static int selinux_sem_alloc_security(struct sem_array *sma)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
        int rc;
 
        rc = ipc_alloc_security(current, &sma->sem_perm, SECCLASS_SEM);
        if (rc)
                return rc;
 
-       tsec = current->security;
        isec = sma->sem_perm.security;
 
        AVC_AUDIT_DATA_INIT(&ad, IPC);
        ad.u.ipc_id = sma->sem_perm.key;
 
-       rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
+       rc = avc_has_perm(sid, isec->sid, SECCLASS_SEM,
                          SEM__CREATE, &ad);
        if (rc) {
                ipc_free_security(&sma->sem_perm);
@@ -5112,17 +5210,16 @@ static void selinux_sem_free_security(struct sem_array *sma)
 
 static int selinux_sem_associate(struct sem_array *sma, int semflg)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
 
-       tsec = current->security;
        isec = sma->sem_perm.security;
 
        AVC_AUDIT_DATA_INIT(&ad, IPC);
        ad.u.ipc_id = sma->sem_perm.key;
 
-       return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
+       return avc_has_perm(sid, isec->sid, SECCLASS_SEM,
                            SEM__ASSOCIATE, &ad);
 }
 
@@ -5212,33 +5309,35 @@ static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode)
 static int selinux_getprocattr(struct task_struct *p,
                               char *name, char **value)
 {
-       struct task_security_struct *tsec;
+       const struct task_security_struct *__tsec;
        u32 sid;
        int error;
        unsigned len;
 
        if (current != p) {
-               error = task_has_perm(current, p, PROCESS__GETATTR);
+               error = current_has_perm(p, PROCESS__GETATTR);
                if (error)
                        return error;
        }
 
-       tsec = p->security;
+       rcu_read_lock();
+       __tsec = __task_cred(p)->security;
 
        if (!strcmp(name, "current"))
-               sid = tsec->sid;
+               sid = __tsec->sid;
        else if (!strcmp(name, "prev"))
-               sid = tsec->osid;
+               sid = __tsec->osid;
        else if (!strcmp(name, "exec"))
-               sid = tsec->exec_sid;
+               sid = __tsec->exec_sid;
        else if (!strcmp(name, "fscreate"))
-               sid = tsec->create_sid;
+               sid = __tsec->create_sid;
        else if (!strcmp(name, "keycreate"))
-               sid = tsec->keycreate_sid;
+               sid = __tsec->keycreate_sid;
        else if (!strcmp(name, "sockcreate"))
-               sid = tsec->sockcreate_sid;
+               sid = __tsec->sockcreate_sid;
        else
-               return -EINVAL;
+               goto invalid;
+       rcu_read_unlock();
 
        if (!sid)
                return 0;
@@ -5247,6 +5346,10 @@ static int selinux_getprocattr(struct task_struct *p,
        if (error)
                return error;
        return len;
+
+invalid:
+       rcu_read_unlock();
+       return -EINVAL;
 }
 
 static int selinux_setprocattr(struct task_struct *p,
@@ -5254,7 +5357,8 @@ static int selinux_setprocattr(struct task_struct *p,
 {
        struct task_security_struct *tsec;
        struct task_struct *tracer;
-       u32 sid = 0;
+       struct cred *new;
+       u32 sid = 0, ptsid;
        int error;
        char *str = value;
 
@@ -5270,15 +5374,15 @@ static int selinux_setprocattr(struct task_struct *p,
         * above restriction is ever removed.
         */
        if (!strcmp(name, "exec"))
-               error = task_has_perm(current, p, PROCESS__SETEXEC);
+               error = current_has_perm(p, PROCESS__SETEXEC);
        else if (!strcmp(name, "fscreate"))
-               error = task_has_perm(current, p, PROCESS__SETFSCREATE);
+               error = current_has_perm(p, PROCESS__SETFSCREATE);
        else if (!strcmp(name, "keycreate"))
-               error = task_has_perm(current, p, PROCESS__SETKEYCREATE);
+               error = current_has_perm(p, PROCESS__SETKEYCREATE);
        else if (!strcmp(name, "sockcreate"))
-               error = task_has_perm(current, p, PROCESS__SETSOCKCREATE);
+               error = current_has_perm(p, PROCESS__SETSOCKCREATE);
        else if (!strcmp(name, "current"))
-               error = task_has_perm(current, p, PROCESS__SETCURRENT);
+               error = current_has_perm(p, PROCESS__SETCURRENT);
        else
                error = -EINVAL;
        if (error)
@@ -5301,87 +5405,75 @@ static int selinux_setprocattr(struct task_struct *p,
                        return error;
        }
 
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+
        /* Permission checking based on the specified context is
           performed during the actual operation (execve,
           open/mkdir/...), when we know the full context of the
-          operation.  See selinux_bprm_set_security for the execve
+          operation.  See selinux_bprm_set_creds for the execve
           checks and may_create for the file creation checks. The
           operation will then fail if the context is not permitted. */
-       tsec = p->security;
-       if (!strcmp(name, "exec"))
+       tsec = new->security;
+       if (!strcmp(name, "exec")) {
                tsec->exec_sid = sid;
-       else if (!strcmp(name, "fscreate"))
+       } else if (!strcmp(name, "fscreate")) {
                tsec->create_sid = sid;
-       else if (!strcmp(name, "keycreate")) {
+       else if (!strcmp(name, "keycreate")) {
                error = may_create_key(sid, p);
                if (error)
-                       return error;
+                       goto abort_change;
                tsec->keycreate_sid = sid;
-       } else if (!strcmp(name, "sockcreate"))
+       } else if (!strcmp(name, "sockcreate")) {
                tsec->sockcreate_sid = sid;
-       else if (!strcmp(name, "current")) {
-               struct av_decision avd;
-
+       } else if (!strcmp(name, "current")) {
+               error = -EINVAL;
                if (sid == 0)
-                       return -EINVAL;
-               /*
-                * SELinux allows to change context in the following case only.
-                *  - Single threaded processes.
-                *  - Multi threaded processes intend to change its context into
-                *    more restricted domain (defined by TYPEBOUNDS statement).
-                */
-               if (atomic_read(&p->mm->mm_users) != 1) {
-                       struct task_struct *g, *t;
-                       struct mm_struct *mm = p->mm;
-                       read_lock(&tasklist_lock);
-                       do_each_thread(g, t) {
-                               if (t->mm == mm && t != p) {
-                                       read_unlock(&tasklist_lock);
-                                       error = security_bounded_transition(tsec->sid, sid);
-                                       if (!error)
-                                               goto boundary_ok;
-
-                                       return error;
-                               }
-                       } while_each_thread(g, t);
-                       read_unlock(&tasklist_lock);
+                       goto abort_change;
+
+               /* Only allow single threaded processes to change context */
+               error = -EPERM;
+               if (!is_single_threaded(p)) {
+                       error = security_bounded_transition(tsec->sid, sid);
+                       if (error)
+                               goto abort_change;
                }
-boundary_ok:
 
                /* Check permissions for the transition. */
                error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
                                     PROCESS__DYNTRANSITION, NULL);
                if (error)
-                       return error;
+                       goto abort_change;
 
                /* Check for ptracing, and update the task SID if ok.
                   Otherwise, leave SID unchanged and fail. */
+               ptsid = 0;
                task_lock(p);
-               rcu_read_lock();
                tracer = tracehook_tracer_task(p);
-               if (tracer != NULL) {
-                       struct task_security_struct *ptsec = tracer->security;
-                       u32 ptsid = ptsec->sid;
-                       rcu_read_unlock();
-                       error = avc_has_perm_noaudit(ptsid, sid,
-                                                    SECCLASS_PROCESS,
-                                                    PROCESS__PTRACE, 0, &avd);
-                       if (!error)
-                               tsec->sid = sid;
-                       task_unlock(p);
-                       avc_audit(ptsid, sid, SECCLASS_PROCESS,
-                                 PROCESS__PTRACE, &avd, error, NULL);
+               if (tracer)
+                       ptsid = task_sid(tracer);
+               task_unlock(p);
+
+               if (tracer) {
+                       error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
+                                            PROCESS__PTRACE, NULL);
                        if (error)
-                               return error;
-               } else {
-                       rcu_read_unlock();
-                       tsec->sid = sid;
-                       task_unlock(p);
+                               goto abort_change;
                }
-       } else
-               return -EINVAL;
 
+               tsec->sid = sid;
+       } else {
+               error = -EINVAL;
+               goto abort_change;
+       }
+
+       commit_creds(new);
        return size;
+
+abort_change:
+       abort_creds(new);
+       return error;
 }
 
 static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
@@ -5401,22 +5493,23 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
 
 #ifdef CONFIG_KEYS
 
-static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
+static int selinux_key_alloc(struct key *k, const struct cred *cred,
                             unsigned long flags)
 {
-       struct task_security_struct *tsec = tsk->security;
+       const struct task_security_struct *tsec;
        struct key_security_struct *ksec;
 
        ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL);
        if (!ksec)
                return -ENOMEM;
 
+       tsec = cred->security;
        if (tsec->keycreate_sid)
                ksec->sid = tsec->keycreate_sid;
        else
                ksec->sid = tsec->sid;
-       k->security = ksec;
 
+       k->security = ksec;
        return 0;
 }
 
@@ -5429,17 +5522,12 @@ static void selinux_key_free(struct key *k)
 }
 
 static int selinux_key_permission(key_ref_t key_ref,
-                           struct task_struct *ctx,
-                           key_perm_t perm)
+                                 const struct cred *cred,
+                                 key_perm_t perm)
 {
        struct key *key;
-       struct task_security_struct *tsec;
        struct key_security_struct *ksec;
-
-       key = key_ref_to_ptr(key_ref);
-
-       tsec = ctx->security;
-       ksec = key->security;
+       u32 sid;
 
        /* if no specific permissions are requested, we skip the
           permission check. No serious, additional covert channels
@@ -5447,8 +5535,12 @@ static int selinux_key_permission(key_ref_t key_ref,
        if (perm == 0)
                return 0;
 
-       return avc_has_perm(tsec->sid, ksec->sid,
-                           SECCLASS_KEY, perm, NULL);
+       sid = cred_sid(cred);
+
+       key = key_ref_to_ptr(key_ref);
+       ksec = key->security;
+
+       return avc_has_perm(sid, ksec->sid, SECCLASS_KEY, perm, NULL);
 }
 
 static int selinux_key_getsecurity(struct key *key, char **_buffer)
@@ -5473,8 +5565,7 @@ static struct security_operations selinux_ops = {
        .ptrace_may_access =            selinux_ptrace_may_access,
        .ptrace_traceme =               selinux_ptrace_traceme,
        .capget =                       selinux_capget,
-       .capset_check =                 selinux_capset_check,
-       .capset_set =                   selinux_capset_set,
+       .capset =                       selinux_capset,
        .sysctl =                       selinux_sysctl,
        .capable =                      selinux_capable,
        .quotactl =                     selinux_quotactl,
@@ -5485,12 +5576,10 @@ static struct security_operations selinux_ops = {
        .netlink_send =                 selinux_netlink_send,
        .netlink_recv =                 selinux_netlink_recv,
 
-       .bprm_alloc_security =          selinux_bprm_alloc_security,
-       .bprm_free_security =           selinux_bprm_free_security,
-       .bprm_apply_creds =             selinux_bprm_apply_creds,
-       .bprm_post_apply_creds =        selinux_bprm_post_apply_creds,
-       .bprm_set_security =            selinux_bprm_set_security,
+       .bprm_set_creds =               selinux_bprm_set_creds,
        .bprm_check_security =          selinux_bprm_check_security,
+       .bprm_committing_creds =        selinux_bprm_committing_creds,
+       .bprm_committed_creds =         selinux_bprm_committed_creds,
        .bprm_secureexec =              selinux_bprm_secureexec,
 
        .sb_alloc_security =            selinux_sb_alloc_security,
@@ -5549,10 +5638,13 @@ static struct security_operations selinux_ops = {
        .dentry_open =                  selinux_dentry_open,
 
        .task_create =                  selinux_task_create,
-       .task_alloc_security =          selinux_task_alloc_security,
-       .task_free_security =           selinux_task_free_security,
+       .cred_free =                    selinux_cred_free,
+       .cred_prepare =                 selinux_cred_prepare,
+       .cred_commit =                  selinux_cred_commit,
+       .kernel_act_as =                selinux_kernel_act_as,
+       .kernel_create_files_as =       selinux_kernel_create_files_as,
        .task_setuid =                  selinux_task_setuid,
-       .task_post_setuid =             selinux_task_post_setuid,
+       .task_fix_setuid =              selinux_task_fix_setuid,
        .task_setgid =                  selinux_task_setgid,
        .task_setpgid =                 selinux_task_setpgid,
        .task_getpgid =                 selinux_task_getpgid,
@@ -5569,7 +5661,6 @@ static struct security_operations selinux_ops = {
        .task_kill =                    selinux_task_kill,
        .task_wait =                    selinux_task_wait,
        .task_prctl =                   selinux_task_prctl,
-       .task_reparent_to_init =        selinux_task_reparent_to_init,
        .task_to_inode =                selinux_task_to_inode,
 
        .ipc_permission =               selinux_ipc_permission,
@@ -5665,8 +5756,6 @@ static struct security_operations selinux_ops = {
 
 static __init int selinux_init(void)
 {
-       struct task_security_struct *tsec;
-
        if (!security_module_enable(&selinux_ops)) {
                selinux_enabled = 0;
                return 0;
@@ -5680,10 +5769,7 @@ static __init int selinux_init(void)
        printk(KERN_INFO "SELinux:  Initializing.\n");
 
        /* Set the security state for the initial task. */
-       if (task_alloc_security(current))
-               panic("SELinux:  Failed to initialize initial task.\n");
-       tsec = current->security;
-       tsec->osid = tsec->sid = SECINITSID_KERNEL;
+       cred_init_security();
 
        sel_inode_cache = kmem_cache_create("selinux_inode_security",
                                            sizeof(struct inode_security_struct),
index 1223b4f..c0c8854 100644 (file)
    S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NAME_CONNECT, "name_connect")
    S_(SECCLASS_MEMPROTECT, MEMPROTECT__MMAP_ZERO, "mmap_zero")
    S_(SECCLASS_PEER, PEER__RECV, "recv")
+   S_(SECCLASS_KERNEL_SERVICE, KERNEL_SERVICE__USE_AS_OVERRIDE, "use_as_override")
+   S_(SECCLASS_KERNEL_SERVICE, KERNEL_SERVICE__CREATE_FILES_AS, "create_files_as")
index c4c5116..0ba79fe 100644 (file)
 #define DCCP_SOCKET__NAME_CONNECT                 0x00800000UL
 #define MEMPROTECT__MMAP_ZERO                     0x00000001UL
 #define PEER__RECV                                0x00000001UL
+#define KERNEL_SERVICE__USE_AS_OVERRIDE           0x00000001UL
+#define KERNEL_SERVICE__CREATE_FILES_AS           0x00000002UL
index bd813c3..21ec786 100644 (file)
@@ -72,3 +72,8 @@
     S_(NULL)
     S_("peer")
     S_("capability2")
+    S_(NULL)
+    S_(NULL)
+    S_(NULL)
+    S_(NULL)
+    S_("kernel_service")
index febf886..882f27d 100644 (file)
@@ -52,6 +52,7 @@
 #define SECCLASS_MEMPROTECT                              61
 #define SECCLASS_PEER                                    68
 #define SECCLASS_CAPABILITY2                             69
+#define SECCLASS_KERNEL_SERVICE                          74
 
 /*
  * Security identifier indices for initial entities
index f8be8d7..3cc4516 100644 (file)
@@ -77,17 +77,6 @@ struct ipc_security_struct {
        u32 sid;        /* SID of IPC resource */
 };
 
-struct bprm_security_struct {
-       u32 sid;                /* SID for transformed process */
-       unsigned char set;
-
-       /*
-        * unsafe is used to share failure information from bprm_apply_creds()
-        * to bprm_post_apply_creds().
-        */
-       char unsafe;
-};
-
 struct netif_security_struct {
        int ifindex;                    /* device index */
        u32 sid;                        /* SID for this interface */
index ff59c0c..4ed7bab 100644 (file)
@@ -63,6 +63,9 @@ static struct nlmsg_perm nlmsg_route_perms[] =
        { RTM_GETANYCAST,       NETLINK_ROUTE_SOCKET__NLMSG_READ  },
        { RTM_GETNEIGHTBL,      NETLINK_ROUTE_SOCKET__NLMSG_READ  },
        { RTM_SETNEIGHTBL,      NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+       { RTM_NEWADDRLABEL,     NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+       { RTM_DELADDRLABEL,     NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+       { RTM_GETADDRLABEL,     NETLINK_ROUTE_SOCKET__NLMSG_READ  },
 };
 
 static struct nlmsg_perm nlmsg_firewall_perms[] =
index 69c9dcc..c863036 100644 (file)
@@ -95,13 +95,18 @@ extern void selnl_notify_setenforce(int val);
 static int task_has_security(struct task_struct *tsk,
                             u32 perms)
 {
-       struct task_security_struct *tsec;
-
-       tsec = tsk->security;
+       const struct task_security_struct *tsec;
+       u32 sid = 0;
+
+       rcu_read_lock();
+       tsec = __task_cred(tsk)->security;
+       if (tsec)
+               sid = tsec->sid;
+       rcu_read_unlock();
        if (!tsec)
                return -EACCES;
 
-       return avc_has_perm(tsec->sid, SECINITSID_SECURITY,
+       return avc_has_perm(sid, SECINITSID_SECURITY,
                            SECCLASS_SECURITY, perms, NULL);
 }
 
index 8f17f54..c0eb720 100644 (file)
@@ -197,7 +197,7 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
        struct xfrm_user_sec_ctx *uctx, u32 sid)
 {
        int rc = 0;
-       struct task_security_struct *tsec = current->security;
+       const struct task_security_struct *tsec = current_security();
        struct xfrm_sec_ctx *ctx = NULL;
        char *ctx_str = NULL;
        u32 str_len;
@@ -333,7 +333,7 @@ void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx)
  */
 int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)
 {
-       struct task_security_struct *tsec = current->security;
+       const struct task_security_struct *tsec = current_security();
        int rc = 0;
 
        if (ctx) {
@@ -378,7 +378,7 @@ void selinux_xfrm_state_free(struct xfrm_state *x)
   */
 int selinux_xfrm_state_delete(struct xfrm_state *x)
 {
-       struct task_security_struct *tsec = current->security;
+       const struct task_security_struct *tsec = current_security();
        struct xfrm_sec_ctx *ctx = x->security;
        int rc = 0;
 
index 79ff21e..247cec3 100644 (file)
@@ -164,7 +164,7 @@ int smk_curacc(char *obj_label, u32 mode)
 {
        int rc;
 
-       rc = smk_access(current->security, obj_label, mode);
+       rc = smk_access(current_security(), obj_label, mode);
        if (rc == 0)
                return 0;
 
@@ -173,7 +173,7 @@ int smk_curacc(char *obj_label, u32 mode)
         * only one that gets privilege and current does not
         * have that label.
         */
-       if (smack_onlycap != NULL && smack_onlycap != current->security)
+       if (smack_onlycap != NULL && smack_onlycap != current->cred->security)
                return rc;
 
        if (capable(CAP_MAC_OVERRIDE))
index 6e2dc0b..1b5551d 100644 (file)
@@ -30,6 +30,8 @@
 
 #include "smack.h"
 
+#define task_security(task)    (task_cred_xxx((task), security))
+
 /*
  * I hope these are the hokeyist lines of code in the module. Casey.
  */
@@ -102,7 +104,7 @@ static int smack_ptrace_may_access(struct task_struct *ctp, unsigned int mode)
        if (rc != 0)
                return rc;
 
-       rc = smk_access(current->security, ctp->security, MAY_READWRITE);
+       rc = smk_access(current_security(), task_security(ctp), MAY_READWRITE);
        if (rc != 0 && capable(CAP_MAC_OVERRIDE))
                return 0;
        return rc;
@@ -124,7 +126,7 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
        if (rc != 0)
                return rc;
 
-       rc = smk_access(ptp->security, current->security, MAY_READWRITE);
+       rc = smk_access(task_security(ptp), current_security(), MAY_READWRITE);
        if (rc != 0 && has_capability(ptp, CAP_MAC_OVERRIDE))
                return 0;
        return rc;
@@ -141,7 +143,7 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
 static int smack_syslog(int type)
 {
        int rc;
-       char *sp = current->security;
+       char *sp = current_security();
 
        rc = cap_syslog(type);
        if (rc != 0)
@@ -248,11 +250,12 @@ static int smack_sb_copy_data(char *orig, char *smackopts)
 /**
  * smack_sb_kern_mount - Smack specific mount processing
  * @sb: the file system superblock
+ * @flags: the mount flags
  * @data: the smack mount options
  *
  * Returns 0 on success, an error code on failure
  */
-static int smack_sb_kern_mount(struct super_block *sb, void *data)
+static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
 {
        struct dentry *root = sb->s_root;
        struct inode *inode = root->d_inode;
@@ -373,7 +376,7 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags)
  */
 static int smack_inode_alloc_security(struct inode *inode)
 {
-       inode->i_security = new_inode_smack(current->security);
+       inode->i_security = new_inode_smack(current_security());
        if (inode->i_security == NULL)
                return -ENOMEM;
        return 0;
@@ -818,7 +821,7 @@ static int smack_file_permission(struct file *file, int mask)
  */
 static int smack_file_alloc_security(struct file *file)
 {
-       file->f_security = current->security;
+       file->f_security = current_security();
        return 0;
 }
 
@@ -916,7 +919,7 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd,
  */
 static int smack_file_set_fowner(struct file *file)
 {
-       file->f_security = current->security;
+       file->f_security = current_security();
        return 0;
 }
 
@@ -941,7 +944,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk,
         * struct fown_struct is never outside the context of a struct file
         */
        file = container_of(fown, struct file, f_owner);
-       rc = smk_access(file->f_security, tsk->security, MAY_WRITE);
+       rc = smk_access(file->f_security, tsk->cred->security, MAY_WRITE);
        if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE))
                return 0;
        return rc;
@@ -973,33 +976,75 @@ static int smack_file_receive(struct file *file)
  */
 
 /**
- * smack_task_alloc_security - "allocate" a task blob
- * @tsk: the task in need of a blob
+ * smack_cred_free - "free" task-level security credentials
+ * @cred: the credentials in question
  *
  * Smack isn't using copies of blobs. Everyone
- * points to an immutable list. No alloc required.
- * No data copy required.
+ * points to an immutable list. The blobs never go away.
+ * There is no leak here.
+ */
+static void smack_cred_free(struct cred *cred)
+{
+       cred->security = NULL;
+}
+
+/**
+ * smack_cred_prepare - prepare new set of credentials for modification
+ * @new: the new credentials
+ * @old: the original credentials
+ * @gfp: the atomicity of any memory allocations
+ *
+ * Prepare a new set of credentials for modification.
+ */
+static int smack_cred_prepare(struct cred *new, const struct cred *old,
+                             gfp_t gfp)
+{
+       new->security = old->security;
+       return 0;
+}
+
+/*
+ * commit new credentials
+ * @new: the new credentials
+ * @old: the original credentials
+ */
+static void smack_cred_commit(struct cred *new, const struct cred *old)
+{
+}
+
+/**
+ * smack_kernel_act_as - Set the subjective context in a set of credentials
+ * @new points to the set of credentials to be modified.
+ * @secid specifies the security ID to be set
  *
- * Always returns 0
+ * Set the security data for a kernel service.
  */
-static int smack_task_alloc_security(struct task_struct *tsk)
+static int smack_kernel_act_as(struct cred *new, u32 secid)
 {
-       tsk->security = current->security;
+       char *smack = smack_from_secid(secid);
+
+       if (smack == NULL)
+               return -EINVAL;
 
+       new->security = smack;
        return 0;
 }
 
 /**
- * smack_task_free_security - "free" a task blob
- * @task: the task with the blob
+ * smack_kernel_create_files_as - Set the file creation label in a set of creds
+ * @new points to the set of credentials to be modified
+ * @inode points to the inode to use as a reference
  *
- * Smack isn't using copies of blobs. Everyone
- * points to an immutable list. The blobs never go away.
- * There is no leak here.
+ * Set the file creation context in a set of credentials to the same
+ * as the objective context of the specified inode
  */
-static void smack_task_free_security(struct task_struct *task)
+static int smack_kernel_create_files_as(struct cred *new,
+                                       struct inode *inode)
 {
-       task->security = NULL;
+       struct inode_smack *isp = inode->i_security;
+
+       new->security = isp->smk_inode;
+       return 0;
 }
 
 /**
@@ -1011,7 +1056,7 @@ static void smack_task_free_security(struct task_struct *task)
  */
 static int smack_task_setpgid(struct task_struct *p, pid_t pgid)
 {
-       return smk_curacc(p->security, MAY_WRITE);
+       return smk_curacc(task_security(p), MAY_WRITE);
 }
 
 /**
@@ -1022,7 +1067,7 @@ static int smack_task_setpgid(struct task_struct *p, pid_t pgid)
  */
 static int smack_task_getpgid(struct task_struct *p)
 {
-       return smk_curacc(p->security, MAY_READ);
+       return smk_curacc(task_security(p), MAY_READ);
 }
 
 /**
@@ -1033,7 +1078,7 @@ static int smack_task_getpgid(struct task_struct *p)
  */
 static int smack_task_getsid(struct task_struct *p)
 {
-       return smk_curacc(p->security, MAY_READ);
+       return smk_curacc(task_security(p), MAY_READ);
 }
 
 /**
@@ -1045,7 +1090,7 @@ static int smack_task_getsid(struct task_struct *p)
  */
 static void smack_task_getsecid(struct task_struct *p, u32 *secid)
 {
-       *secid = smack_to_secid(p->security);
+       *secid = smack_to_secid(task_security(p));
 }
 
 /**
@@ -1061,7 +1106,7 @@ static int smack_task_setnice(struct task_struct *p, int nice)
 
        rc = cap_task_setnice(p, nice);
        if (rc == 0)
-               rc = smk_curacc(p->security, MAY_WRITE);
+               rc = smk_curacc(task_security(p), MAY_WRITE);
        return rc;
 }
 
@@ -1078,7 +1123,7 @@ static int smack_task_setioprio(struct task_struct *p, int ioprio)
 
        rc = cap_task_setioprio(p, ioprio);
        if (rc == 0)
-               rc = smk_curacc(p->security, MAY_WRITE);
+               rc = smk_curacc(task_security(p), MAY_WRITE);
        return rc;
 }
 
@@ -1090,7 +1135,7 @@ static int smack_task_setioprio(struct task_struct *p, int ioprio)
  */
 static int smack_task_getioprio(struct task_struct *p)
 {
-       return smk_curacc(p->security, MAY_READ);
+       return smk_curacc(task_security(p), MAY_READ);
 }
 
 /**
@@ -1108,7 +1153,7 @@ static int smack_task_setscheduler(struct task_struct *p, int policy,
 
        rc = cap_task_setscheduler(p, policy, lp);
        if (rc == 0)
-               rc = smk_curacc(p->security, MAY_WRITE);
+               rc = smk_curacc(task_security(p), MAY_WRITE);
        return rc;
 }
 
@@ -1120,7 +1165,7 @@ static int smack_task_setscheduler(struct task_struct *p, int policy,
  */
 static int smack_task_getscheduler(struct task_struct *p)
 {
-       return smk_curacc(p->security, MAY_READ);
+       return smk_curacc(task_security(p), MAY_READ);
 }
 
 /**
@@ -1131,7 +1176,7 @@ static int smack_task_getscheduler(struct task_struct *p)
  */
 static int smack_task_movememory(struct task_struct *p)
 {
-       return smk_curacc(p->security, MAY_WRITE);
+       return smk_curacc(task_security(p), MAY_WRITE);
 }
 
 /**
@@ -1154,13 +1199,13 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info,
         * can write the receiver.
         */
        if (secid == 0)
-               return smk_curacc(p->security, MAY_WRITE);
+               return smk_curacc(task_security(p), MAY_WRITE);
        /*
         * If the secid isn't 0 we're dealing with some USB IO
         * specific behavior. This is not clean. For one thing
         * we can't take privilege into account.
         */
-       return smk_access(smack_from_secid(secid), p->security, MAY_WRITE);
+       return smk_access(smack_from_secid(secid), task_security(p), MAY_WRITE);
 }
 
 /**
@@ -1173,7 +1218,7 @@ static int smack_task_wait(struct task_struct *p)
 {
        int rc;
 
-       rc = smk_access(current->security, p->security, MAY_WRITE);
+       rc = smk_access(current_security(), task_security(p), MAY_WRITE);
        if (rc == 0)
                return 0;
 
@@ -1204,7 +1249,7 @@ static int smack_task_wait(struct task_struct *p)
 static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
 {
        struct inode_smack *isp = inode->i_security;
-       isp->smk_inode = p->security;
+       isp->smk_inode = task_security(p);
 }
 
 /*
@@ -1223,7 +1268,7 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
  */
 static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
 {
-       char *csp = current->security;
+       char *csp = current_security();
        struct socket_smack *ssp;
 
        ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
@@ -1448,7 +1493,7 @@ static int smack_flags_to_may(int flags)
  */
 static int smack_msg_msg_alloc_security(struct msg_msg *msg)
 {
-       msg->security = current->security;
+       msg->security = current_security();
        return 0;
 }
 
@@ -1484,7 +1529,7 @@ static int smack_shm_alloc_security(struct shmid_kernel *shp)
 {
        struct kern_ipc_perm *isp = &shp->shm_perm;
 
-       isp->security = current->security;
+       isp->security = current_security();
        return 0;
 }
 
@@ -1593,7 +1638,7 @@ static int smack_sem_alloc_security(struct sem_array *sma)
 {
        struct kern_ipc_perm *isp = &sma->sem_perm;
 
-       isp->security = current->security;
+       isp->security = current_security();
        return 0;
 }
 
@@ -1697,7 +1742,7 @@ static int smack_msg_queue_alloc_security(struct msg_queue *msq)
 {
        struct kern_ipc_perm *kisp = &msq->q_perm;
 
-       kisp->security = current->security;
+       kisp->security = current_security();
        return 0;
 }
 
@@ -1852,7 +1897,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
        struct super_block *sbp;
        struct superblock_smack *sbsp;
        struct inode_smack *isp;
-       char *csp = current->security;
+       char *csp = current_security();
        char *fetched;
        char *final;
        struct dentry *dp;
@@ -2009,7 +2054,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
        if (strcmp(name, "current") != 0)
                return -EINVAL;
 
-       cp = kstrdup(p->security, GFP_KERNEL);
+       cp = kstrdup(task_security(p), GFP_KERNEL);
        if (cp == NULL)
                return -ENOMEM;
 
@@ -2033,6 +2078,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
 static int smack_setprocattr(struct task_struct *p, char *name,
                             void *value, size_t size)
 {
+       struct cred *new;
        char *newsmack;
 
        /*
@@ -2055,7 +2101,11 @@ static int smack_setprocattr(struct task_struct *p, char *name,
        if (newsmack == NULL)
                return -EINVAL;
 
-       p->security = newsmack;
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+       new->security = newsmack;
+       commit_creds(new);
        return size;
 }
 
@@ -2288,8 +2338,7 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent)
                return;
 
        ssp = sk->sk_security;
-       ssp->smk_in = current->security;
-       ssp->smk_out = current->security;
+       ssp->smk_in = ssp->smk_out = current_security();
        ssp->smk_packet[0] = '\0';
 
        rc = smack_netlabel(sk);
@@ -2352,17 +2401,17 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 /**
  * smack_key_alloc - Set the key security blob
  * @key: object
- * @tsk: the task associated with the key
+ * @cred: the credentials to use
  * @flags: unused
  *
  * No allocation required
  *
  * Returns 0
  */
-static int smack_key_alloc(struct key *key, struct task_struct *tsk,
+static int smack_key_alloc(struct key *key, const struct cred *cred,
                           unsigned long flags)
 {
-       key->security = tsk->security;
+       key->security = cred->security;
        return 0;
 }
 
@@ -2380,14 +2429,14 @@ static void smack_key_free(struct key *key)
 /*
  * smack_key_permission - Smack access on a key
  * @key_ref: gets to the object
- * @context: task involved
+ * @cred: the credentials to use
  * @perm: unused
  *
  * Return 0 if the task has read and write to the object,
  * an error code otherwise
  */
 static int smack_key_permission(key_ref_t key_ref,
-                               struct task_struct *context, key_perm_t perm)
+                               const struct cred *cred, key_perm_t perm)
 {
        struct key *keyp;
 
@@ -2403,10 +2452,10 @@ static int smack_key_permission(key_ref_t key_ref,
        /*
         * This should not occur
         */
-       if (context->security == NULL)
+       if (cred->security == NULL)
                return -EACCES;
 
-       return smk_access(context->security, keyp->security, MAY_READWRITE);
+       return smk_access(cred->security, keyp->security, MAY_READWRITE);
 }
 #endif /* CONFIG_KEYS */
 
@@ -2577,15 +2626,13 @@ struct security_operations smack_ops = {
        .ptrace_may_access =            smack_ptrace_may_access,
        .ptrace_traceme =               smack_ptrace_traceme,
        .capget =                       cap_capget,
-       .capset_check =                 cap_capset_check,
-       .capset_set =                   cap_capset_set,
+       .capset =                       cap_capset,
        .capable =                      cap_capable,
        .syslog =                       smack_syslog,
        .settime =                      cap_settime,
        .vm_enough_memory =             cap_vm_enough_memory,
 
-       .bprm_apply_creds =             cap_bprm_apply_creds,
-       .bprm_set_security =            cap_bprm_set_security,
+       .bprm_set_creds =               cap_bprm_set_creds,
        .bprm_secureexec =              cap_bprm_secureexec,
 
        .sb_alloc_security =            smack_sb_alloc_security,
@@ -2627,9 +2674,12 @@ struct security_operations smack_ops = {
        .file_send_sigiotask =          smack_file_send_sigiotask,
        .file_receive =                 smack_file_receive,
 
-       .task_alloc_security =          smack_task_alloc_security,
-       .task_free_security =           smack_task_free_security,
-       .task_post_setuid =             cap_task_post_setuid,
+       .cred_free =                    smack_cred_free,
+       .cred_prepare =                 smack_cred_prepare,
+       .cred_commit =                  smack_cred_commit,
+       .kernel_act_as =                smack_kernel_act_as,
+       .kernel_create_files_as =       smack_kernel_create_files_as,
+       .task_fix_setuid =              cap_task_fix_setuid,
        .task_setpgid =                 smack_task_setpgid,
        .task_getpgid =                 smack_task_getpgid,
        .task_getsid =                  smack_task_getsid,
@@ -2642,7 +2692,6 @@ struct security_operations smack_ops = {
        .task_movememory =              smack_task_movememory,
        .task_kill =                    smack_task_kill,
        .task_wait =                    smack_task_wait,
-       .task_reparent_to_init =        cap_task_reparent_to_init,
        .task_to_inode =                smack_task_to_inode,
        .task_prctl =                   cap_task_prctl,
 
@@ -2718,6 +2767,8 @@ struct security_operations smack_ops = {
  */
 static __init int smack_init(void)
 {
+       struct cred *cred;
+
        if (!security_module_enable(&smack_ops))
                return 0;
 
@@ -2726,7 +2777,8 @@ static __init int smack_init(void)
        /*
         * Set the security state for the initial task.
         */
-       current->security = &smack_known_floor.smk_known;
+       cred = (struct cred *) current->cred;
+       cred->security = &smack_known_floor.smk_known;
 
        /*
         * Initialize locks
index c21d8c8..ca257df 100644 (file)
@@ -336,7 +336,7 @@ static void smk_cipso_doi(void)
 
        audit_info.loginuid = audit_get_loginuid(current);
        audit_info.sessionid = audit_get_sessionid(current);
-       audit_info.secid = smack_to_secid(current->security);
+       audit_info.secid = smack_to_secid(current_security());
 
        rc = netlbl_cfg_map_del(NULL, &audit_info);
        if (rc != 0)
@@ -371,7 +371,7 @@ static void smk_unlbl_ambient(char *oldambient)
 
        audit_info.loginuid = audit_get_loginuid(current);
        audit_info.sessionid = audit_get_sessionid(current);
-       audit_info.secid = smack_to_secid(current->security);
+       audit_info.secid = smack_to_secid(current_security());
 
        if (oldambient != NULL) {
                rc = netlbl_cfg_map_del(oldambient, &audit_info);
@@ -843,7 +843,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
                                 size_t count, loff_t *ppos)
 {
        char in[SMK_LABELLEN];
-       char *sp = current->security;
+       char *sp = current->cred->security;
 
        if (!capable(CAP_MAC_ADMIN))
                return -EPERM;
index 7fa37e1..a351dd0 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/init.h>
 #include <linux/device.h>
 #include <linux/string.h>
+#include <sound/ac97_codec.h>
 
 /*
  * Let drivers decide whether they want to support given codec from their
index 31cbe68..c3ee77f 100644 (file)
@@ -1,3 +1,7 @@
+snd-aoa-codec-onyx-objs := onyx.o
+snd-aoa-codec-tas-objs := tas.o
+snd-aoa-codec-toonie-objs := toonie.o
+
 obj-$(CONFIG_SND_AOA_ONYX) += snd-aoa-codec-onyx.o
 obj-$(CONFIG_SND_AOA_TAS) += snd-aoa-codec-tas.o
 obj-$(CONFIG_SND_AOA_TOONIE) += snd-aoa-codec-toonie.o
similarity index 99%
rename from sound/aoa/codecs/snd-aoa-codec-onyx.c
rename to sound/aoa/codecs/onyx.c
index 6a3837d..15500b9 100644 (file)
@@ -37,7 +37,7 @@ MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("pcm3052 (onyx) codec driver for snd-aoa");
 
-#include "snd-aoa-codec-onyx.h"
+#include "onyx.h"
 #include "../aoa.h"
 #include "../soundbus/soundbus.h"
 
@@ -292,7 +292,7 @@ static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol,
 static struct snd_kcontrol_new capture_source_control = {
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
        /* If we name this 'Input Source', it properly shows up in
-        * alsamixer as a selection, * but it's shown under the 
+        * alsamixer as a selection, * but it's shown under the
         * 'Playback' category.
         * If I name it 'Capture Source', it shows up in strange
         * ways (two bools of which one can be selected at a
@@ -477,7 +477,7 @@ static int onyx_spdif_mask_get(struct snd_kcontrol *kcontrol,
 
        ucontrol->value.iec958.status[3] = 0x3f;
        ucontrol->value.iec958.status[4] = 0x0f;
-       
+
        return 0;
 }
 
@@ -682,7 +682,7 @@ static int onyx_usable(struct codec_info_item *cii,
        onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
        spdif_enabled = !!(v & ONYX_SPDIF_ENABLE);
        onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
-       analog_enabled = 
+       analog_enabled =
                (v & (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT))
                 != (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT);
        mutex_unlock(&onyx->mutex);
@@ -882,7 +882,7 @@ static int onyx_init_codec(struct aoa_codec *codec)
        msleep(1);
        onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
        msleep(1);
-       
+
        if (onyx_register_init(onyx)) {
                printk(KERN_ERR PFX "failed to initialise onyx registers\n");
                return -ENODEV;
@@ -1069,7 +1069,7 @@ static int onyx_i2c_attach(struct i2c_adapter *adapter)
 
        /* if that didn't work, try desperate mode for older
         * machines that have stuff missing from the device tree */
-       
+
        if (!of_device_is_compatible(busnode, "k2-i2c"))
                return -ENODEV;
 
similarity index 99%
rename from sound/aoa/codecs/snd-aoa-codec-tas.c
rename to sound/aoa/codecs/tas.c
index 6c515b2..008e0f8 100644 (file)
@@ -71,9 +71,9 @@ MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("tas codec driver for snd-aoa");
 
-#include "snd-aoa-codec-tas.h"
-#include "snd-aoa-codec-tas-gain-table.h"
-#include "snd-aoa-codec-tas-basstreble.h"
+#include "tas.h"
+#include "tas-gain-table.h"
+#include "tas-basstreble.h"
 #include "../aoa.h"
 #include "../soundbus/soundbus.h"
 
@@ -880,7 +880,7 @@ static void tas_exit_codec(struct aoa_codec *codec)
                return;
        tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas);
 }
-       
+
 
 static struct i2c_driver tas_driver;
 
similarity index 98%
rename from sound/aoa/codecs/snd-aoa-codec-toonie.c
rename to sound/aoa/codecs/toonie.c
index 3c7d1d8..f13827e 100644 (file)
@@ -131,7 +131,7 @@ static int __init toonie_init(void)
        toonie->codec.owner = THIS_MODULE;
        toonie->codec.init = toonie_init_codec;
        toonie->codec.exit = toonie_exit_codec;
-                                        
+
        if (aoa_codec_register(&toonie->codec)) {
                kfree(toonie);
                return -EINVAL;
index 62dc728..a1596e8 100644 (file)
@@ -1,5 +1,5 @@
 obj-$(CONFIG_SND_AOA) += snd-aoa.o
-snd-aoa-objs := snd-aoa-core.o \
-               snd-aoa-alsa.o \
-               snd-aoa-gpio-pmf.o \
-               snd-aoa-gpio-feature.o
+snd-aoa-objs := core.o \
+               alsa.o \
+               gpio-pmf.o \
+               gpio-feature.o
similarity index 98%
rename from sound/aoa/core/snd-aoa-alsa.c
rename to sound/aoa/core/alsa.c
index 17fe689..6178504 100644 (file)
@@ -6,7 +6,7 @@
  * GPL v2, can be found in COPYING.
  */
 #include <linux/module.h>
-#include "snd-aoa-alsa.h"
+#include "alsa.h"
 
 static int index = -1;
 module_param(index, int, 0444);
@@ -64,7 +64,7 @@ int aoa_snd_device_new(snd_device_type_t type,
 {
        struct snd_card *card = aoa_get_card();
        int err;
-       
+
        if (!card) return -ENOMEM;
 
        err = snd_device_new(card, type, device_data, ops);
similarity index 99%
rename from sound/aoa/core/snd-aoa-core.c
rename to sound/aoa/core/core.c
index 19fdae4..10bec6c 100644 (file)
@@ -10,7 +10,7 @@
 #include <linux/module.h>
 #include <linux/list.h>
 #include "../aoa.h"
-#include "snd-aoa-alsa.h"
+#include "alsa.h"
 
 MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver");
 MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
similarity index 99%
rename from sound/aoa/core/snd-aoa-gpio-feature.c
rename to sound/aoa/core/gpio-feature.c
index 805dcbf..c93ad5d 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL v2, can be found in COPYING.
  *
- * This file contains the GPIO control routines for 
+ * This file contains the GPIO control routines for
  * direct (through feature calls) access to the GPIO
  * registers.
  */
index 55fc5e7..da37c10 100644 (file)
@@ -1 +1,3 @@
+snd-aoa-fabric-layout-objs += layout.o
+
 obj-$(CONFIG_SND_AOA_FABRIC_LAYOUT) += snd-aoa-fabric-layout.o
similarity index 99%
rename from sound/aoa/fabrics/snd-aoa-fabric-layout.c
rename to sound/aoa/fabrics/layout.c
index dea7abb..ad60f5d 100644 (file)
@@ -66,7 +66,7 @@ struct layout {
        unsigned int layout_id;
        struct codec_connect_info codecs[MAX_CODECS_PER_BUS];
        int flags;
-       
+
        /* if busname is not assigned, we use 'Master' below,
         * so that our layout table doesn't need to be filled
         * too much.
index e57a5cf..1b949b2 100644 (file)
@@ -1,2 +1,2 @@
 obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += snd-aoa-i2sbus.o
-snd-aoa-i2sbus-objs := i2sbus-core.o i2sbus-pcm.o i2sbus-control.o
+snd-aoa-i2sbus-objs := core.o pcm.o control.o
similarity index 99%
rename from sound/aoa/soundbus/i2sbus/i2sbus-core.c
rename to sound/aoa/soundbus/i2sbus/core.c
index b4590df..be468ed 100644 (file)
@@ -64,7 +64,7 @@ static void free_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev,
                                       struct dbdma_command_mem *r)
 {
        if (!r->space) return;
-       
+
        dma_free_coherent(&macio_get_pci_dev(i2sdev->macio)->dev,
                            r->size, r->space, r->bus_addr);
 }
@@ -247,7 +247,7 @@ static int i2sbus_add_dev(struct macio_dev *macio,
                 * but request_resource doesn't know about parents and
                 * contained resources...
                 */
-               dev->allocated_resource[i] = 
+               dev->allocated_resource[i] =
                        request_mem_region(dev->resources[i].start,
                                           dev->resources[i].end -
                                           dev->resources[i].start + 1,
index ff29654..befefd9 100644 (file)
@@ -18,7 +18,7 @@
 #include <asm/pmac_feature.h>
 #include <asm/dbdma.h>
 
-#include "i2sbus-interface.h"
+#include "interface.h"
 #include "../soundbus.h"
 
 struct i2sbus_control {
index 66348c9..7bbdda0 100644 (file)
@@ -95,6 +95,26 @@ config SND_SEQUENCER_OSS
          this will be compiled as a module. The module will be called
          snd-seq-oss.
 
+config SND_HRTIMER
+       tristate "HR-timer backend support"
+       depends on HIGH_RES_TIMERS
+       select SND_TIMER
+       help
+         Say Y here to enable HR-timer backend for ALSA timer.  ALSA uses
+         the hrtimer as a precise timing source. The ALSA sequencer code
+         also can use this timing source.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-hrtimer.
+
+config SND_SEQ_HRTIMER_DEFAULT
+       bool "Use HR-timer as default sequencer timer"
+       depends on SND_HRTIMER && SND_SEQUENCER
+       default y
+       help
+         Say Y here to use the HR-timer backend as the default sequencer
+         timer.
+
 config SND_RTCTIMER
        tristate "RTC Timer support"
        depends on RTC
@@ -114,6 +134,7 @@ config SND_RTCTIMER
 config SND_SEQ_RTCTIMER_DEFAULT
        bool "Use RTC as default sequencer timer"
        depends on SND_RTCTIMER && SND_SEQUENCER
+       depends on !SND_SEQ_HRTIMER_DEFAULT
        default y
        help
          Say Y here to use the RTC timer as the default sequencer
index d57125a..4229052 100644 (file)
@@ -17,12 +17,14 @@ snd-page-alloc-$(CONFIG_HAS_DMA) += sgbuf.o
 
 snd-rawmidi-objs  := rawmidi.o
 snd-timer-objs    := timer.o
+snd-hrtimer-objs  := hrtimer.o
 snd-rtctimer-objs := rtctimer.o
 snd-hwdep-objs    := hwdep.o
 
 obj-$(CONFIG_SND)              += snd.o
 obj-$(CONFIG_SND_HWDEP)                += snd-hwdep.o
 obj-$(CONFIG_SND_TIMER)                += snd-timer.o
+obj-$(CONFIG_SND_HRTIMER)      += snd-hrtimer.o
 obj-$(CONFIG_SND_RTCTIMER)     += snd-rtctimer.o
 obj-$(CONFIG_SND_PCM)          += snd-pcm.o snd-page-alloc.o
 obj-$(CONFIG_SND_RAWMIDI)      += snd-rawmidi.o
index c58d822..a67dfac 100644 (file)
@@ -98,7 +98,7 @@ int snd_device_free(struct snd_card *card, void *device_data)
                kfree(dev);
                return 0;
        }
-       snd_printd("device free %p (from %p), not found\n", device_data,
+       snd_printd("device free %p (from %pF), not found\n", device_data,
                   __builtin_return_address(0));
        return -ENXIO;
 }
@@ -135,7 +135,7 @@ int snd_device_disconnect(struct snd_card *card, void *device_data)
                }
                return 0;
        }
-       snd_printd("device disconnect %p (from %p), not found\n", device_data,
+       snd_printd("device disconnect %p (from %pF), not found\n", device_data,
                   __builtin_return_address(0));
        return -ENXIO;
 }
diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c
new file mode 100644 (file)
index 0000000..c1d2859
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * ALSA timer back-end using hrtimer
+ * Copyright (C) 2008 Takashi Iwai
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/hrtimer.h>
+#include <sound/core.h>
+#include <sound/timer.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ALSA hrtimer backend");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_HRTIMER));
+
+#define NANO_SEC       1000000000UL    /* 10^9 in sec */
+static unsigned int resolution;
+
+struct snd_hrtimer {
+       struct snd_timer *timer;
+       struct hrtimer hrt;
+};
+
+static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
+{
+       struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt);
+       struct snd_timer *t = stime->timer;
+       hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution));
+       snd_timer_interrupt(stime->timer, t->sticks);
+       return HRTIMER_RESTART;
+}
+
+static int snd_hrtimer_open(struct snd_timer *t)
+{
+       struct snd_hrtimer *stime;
+
+       stime = kmalloc(sizeof(*stime), GFP_KERNEL);
+       if (!stime)
+               return -ENOMEM;
+       hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+       stime->timer = t;
+       stime->hrt.cb_mode = HRTIMER_CB_IRQSAFE_UNLOCKED;
+       stime->hrt.function = snd_hrtimer_callback;
+       t->private_data = stime;
+       return 0;
+}
+
+static int snd_hrtimer_close(struct snd_timer *t)
+{
+       struct snd_hrtimer *stime = t->private_data;
+
+       if (stime) {
+               hrtimer_cancel(&stime->hrt);
+               kfree(stime);
+               t->private_data = NULL;
+       }
+       return 0;
+}
+
+static int snd_hrtimer_start(struct snd_timer *t)
+{
+       struct snd_hrtimer *stime = t->private_data;
+
+       hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution),
+                     HRTIMER_MODE_REL);
+       return 0;
+}
+
+static int snd_hrtimer_stop(struct snd_timer *t)
+{
+       struct snd_hrtimer *stime = t->private_data;
+
+       hrtimer_cancel(&stime->hrt);
+       return 0;
+}
+
+static struct snd_timer_hardware hrtimer_hw = {
+       .flags =        SNDRV_TIMER_HW_AUTO,
+       .open =         snd_hrtimer_open,
+       .close =        snd_hrtimer_close,
+       .start =        snd_hrtimer_start,
+       .stop =         snd_hrtimer_stop,
+};
+
+/*
+ * entry functions
+ */
+
+static struct snd_timer *mytimer;
+
+static int __init snd_hrtimer_init(void)
+{
+       struct snd_timer *timer;
+       struct timespec tp;
+       int err;
+
+       hrtimer_get_res(CLOCK_MONOTONIC, &tp);
+       if (tp.tv_sec > 0 || !tp.tv_nsec) {
+               snd_printk(KERN_ERR
+                          "snd-hrtimer: Invalid resolution %u.%09u",
+                          (unsigned)tp.tv_sec, (unsigned)tp.tv_nsec);
+               return -EINVAL;
+       }
+       resolution = tp.tv_nsec;
+
+       /* Create a new timer and set up the fields */
+       err = snd_timer_global_new("hrtimer", SNDRV_TIMER_GLOBAL_HRTIMER,
+                                  &timer);
+       if (err < 0)
+               return err;
+
+       timer->module = THIS_MODULE;
+       strcpy(timer->name, "HR timer");
+       timer->hw = hrtimer_hw;
+       timer->hw.resolution = resolution;
+       timer->hw.ticks = NANO_SEC / resolution;
+
+       err = snd_timer_global_register(timer);
+       if (err < 0) {
+               snd_timer_global_free(timer);
+               return err;
+       }
+       mytimer = timer; /* remember this */
+
+       return 0;
+}
+
+static void __exit snd_hrtimer_exit(void)
+{
+       if (mytimer) {
+               snd_timer_global_free(mytimer);
+               mytimer = NULL;
+       }
+}
+
+module_init(snd_hrtimer_init);
+module_exit(snd_hrtimer_exit);
index 527b207..70fa871 100644 (file)
@@ -652,6 +652,23 @@ int snd_info_card_register(struct snd_card *card)
        return 0;
 }
 
+/*
+ * called on card->id change
+ */
+void snd_info_card_id_change(struct snd_card *card)
+{
+       mutex_lock(&info_mutex);
+       if (card->proc_root_link) {
+               snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
+               card->proc_root_link = NULL;
+       }
+       if (strcmp(card->id, card->proc_root->name))
+               card->proc_root_link = proc_symlink(card->id,
+                                                   snd_proc_root,
+                                                   card->proc_root->name);
+       mutex_unlock(&info_mutex);
+}
+
 /*
  * de-register the card proc file
  * called from init.c
index b47ff8b..0d5520c 100644 (file)
@@ -533,6 +533,65 @@ static void choose_default_id(struct snd_card *card)
        }
 }
 
+#ifndef CONFIG_SYSFS_DEPRECATED
+static ssize_t
+card_id_show_attr(struct device *dev,
+                 struct device_attribute *attr, char *buf)
+{
+       struct snd_card *card = dev_get_drvdata(dev);
+       return snprintf(buf, PAGE_SIZE, "%s\n", card ? card->id : "(null)");
+}
+
+static ssize_t
+card_id_store_attr(struct device *dev, struct device_attribute *attr,
+                  const char *buf, size_t count)
+{
+       struct snd_card *card = dev_get_drvdata(dev);
+       char buf1[sizeof(card->id)];
+       size_t copy = count > sizeof(card->id) - 1 ?
+                                       sizeof(card->id) - 1 : count;
+       size_t idx;
+       int c;
+
+       for (idx = 0; idx < copy; idx++) {
+               c = buf[idx];
+               if (!isalnum(c) && c != '_' && c != '-')
+                       return -EINVAL;
+       }
+       memcpy(buf1, buf, copy);
+       buf1[copy] = '\0';
+       mutex_lock(&snd_card_mutex);
+       if (!snd_info_check_reserved_words(buf1)) {
+            __exist:
+               mutex_unlock(&snd_card_mutex);
+               return -EEXIST;
+       }
+       for (idx = 0; idx < snd_ecards_limit; idx++) {
+               if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1))
+                       goto __exist;
+       }
+       strcpy(card->id, buf1);
+       snd_info_card_id_change(card);
+       mutex_unlock(&snd_card_mutex);
+
+       return count;
+}
+
+static struct device_attribute card_id_attrs =
+       __ATTR(id, S_IRUGO | S_IWUSR, card_id_show_attr, card_id_store_attr);
+
+static ssize_t
+card_number_show_attr(struct device *dev,
+                    struct device_attribute *attr, char *buf)
+{
+       struct snd_card *card = dev_get_drvdata(dev);
+       return snprintf(buf, PAGE_SIZE, "%i\n", card ? card->number : -1);
+}
+
+static struct device_attribute card_number_attrs =
+       __ATTR(number, S_IRUGO, card_number_show_attr, NULL);
+#endif /* CONFIG_SYSFS_DEPRECATED */
+
 /**
  *  snd_card_register - register the soundcard
  *  @card: soundcard structure
@@ -553,7 +612,7 @@ int snd_card_register(struct snd_card *card)
 #ifndef CONFIG_SYSFS_DEPRECATED
        if (!card->card_dev) {
                card->card_dev = device_create(sound_class, card->dev,
-                                              MKDEV(0, 0), NULL,
+                                              MKDEV(0, 0), card,
                                               "card%i", card->number);
                if (IS_ERR(card->card_dev))
                        card->card_dev = NULL;
@@ -575,6 +634,16 @@ int snd_card_register(struct snd_card *card)
 #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
        if (snd_mixer_oss_notify_callback)
                snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
+#endif
+#ifndef CONFIG_SYSFS_DEPRECATED
+       if (card->card_dev) {
+               err = device_create_file(card->card_dev, &card_id_attrs);
+               if (err < 0)
+                       return err;
+               err = device_create_file(card->card_dev, &card_number_attrs);
+               if (err < 0)
+                       return err;
+       }
 #endif
        return 0;
 }
index bd2d9e6..dd4a12d 100644 (file)
@@ -34,6 +34,7 @@ static int snd_jack_dev_free(struct snd_device *device)
        else
                input_free_device(jack->input_dev);
 
+       kfree(jack->id);
        kfree(jack);
 
        return 0;
@@ -87,7 +88,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
        if (jack == NULL)
                return -ENOMEM;
 
-       jack->id = id;
+       jack->id = kstrdup(id, GFP_KERNEL);
 
        jack->input_dev = input_allocate_device();
        if (jack->input_dev == NULL) {
@@ -102,9 +103,15 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
        if (type & SND_JACK_HEADPHONE)
                input_set_capability(jack->input_dev, EV_SW,
                                     SW_HEADPHONE_INSERT);
+       if (type & SND_JACK_LINEOUT)
+               input_set_capability(jack->input_dev, EV_SW,
+                                    SW_LINEOUT_INSERT);
        if (type & SND_JACK_MICROPHONE)
                input_set_capability(jack->input_dev, EV_SW,
                                     SW_MICROPHONE_INSERT);
+       if (type & SND_JACK_MECHANICAL)
+               input_set_capability(jack->input_dev, EV_SW,
+                                    SW_JACK_PHYSICAL_INSERT);
 
        err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
        if (err < 0)
@@ -153,9 +160,15 @@ void snd_jack_report(struct snd_jack *jack, int status)
        if (jack->type & SND_JACK_HEADPHONE)
                input_report_switch(jack->input_dev, SW_HEADPHONE_INSERT,
                                    status & SND_JACK_HEADPHONE);
+       if (jack->type & SND_JACK_LINEOUT)
+               input_report_switch(jack->input_dev, SW_LINEOUT_INSERT,
+                                   status & SND_JACK_LINEOUT);
        if (jack->type & SND_JACK_MICROPHONE)
                input_report_switch(jack->input_dev, SW_MICROPHONE_INSERT,
                                    status & SND_JACK_MICROPHONE);
+       if (jack->type & SND_JACK_MECHANICAL)
+               input_report_switch(jack->input_dev, SW_JACK_PHYSICAL_INSERT,
+                                   status & SND_JACK_MECHANICAL);
 
        input_sync(jack->input_dev);
 }
index 39672f6..002777b 100644 (file)
@@ -151,7 +151,7 @@ static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *subs
        if (!substream->opened)
                return;
        if (up) {
-               tasklet_hi_schedule(&substream->runtime->tasklet);
+               tasklet_schedule(&substream->runtime->tasklet);
        } else {
                tasklet_kill(&substream->runtime->tasklet);
                substream->ops->trigger(substream, 0);
@@ -908,7 +908,7 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
        }
        if (result > 0) {
                if (runtime->event)
-                       tasklet_hi_schedule(&runtime->tasklet);
+                       tasklet_schedule(&runtime->tasklet);
                else if (snd_rawmidi_ready(substream))
                        wake_up(&runtime->sleep);
        }
index 51e64e3..0851cd1 100644 (file)
@@ -118,7 +118,7 @@ static void rtctimer_tasklet(unsigned long data)
  */
 static void rtctimer_interrupt(void *private_data)
 {
-       tasklet_hi_schedule(private_data);
+       tasklet_schedule(private_data);
 }
 
 
index ee0f840..bf09a5a 100644 (file)
@@ -43,7 +43,9 @@ int seq_default_timer_class = SNDRV_TIMER_CLASS_GLOBAL;
 int seq_default_timer_sclass = SNDRV_TIMER_SCLASS_NONE;
 int seq_default_timer_card = -1;
 int seq_default_timer_device =
-#ifdef CONFIG_SND_SEQ_RTCTIMER_DEFAULT
+#ifdef CONFIG_SND_SEQ_HRTIMER_DEFAULT
+       SNDRV_TIMER_GLOBAL_HRTIMER
+#elif defined(CONFIG_SND_SEQ_RTCTIMER_DEFAULT)
        SNDRV_TIMER_GLOBAL_RTC
 #else
        SNDRV_TIMER_GLOBAL_SYSTEM
index c584408..7965320 100644 (file)
@@ -743,7 +743,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
        spin_unlock_irqrestore(&timer->lock, flags);
 
        if (use_tasklet)
-               tasklet_hi_schedule(&timer->task_queue);
+               tasklet_schedule(&timer->task_queue);
 }
 
 /*
index 255fd18..0bcf146 100644 (file)
@@ -163,7 +163,7 @@ config SND_ML403_AC97CR
 
 config SND_AC97_POWER_SAVE
        bool "AC97 Power-Saving Mode"
-       depends on SND_AC97_CODEC && EXPERIMENTAL
+       depends on SND_AC97_CODEC
        default n
        help
          Say Y here to enable the aggressive power-saving support of
index 1899cf0..2a02f70 100644 (file)
@@ -96,7 +96,7 @@ static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
                return -EINVAL;
 
        hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-       pcsp_chip.timer.cb_mode = HRTIMER_CB_SOFTIRQ;
+       pcsp_chip.timer.cb_mode = HRTIMER_CB_IRQSAFE_UNLOCKED;
        pcsp_chip.timer.function = pcsp_do_timer;
 
        card = snd_card_new(index, id, THIS_MODULE, 0);
@@ -188,10 +188,8 @@ static int __devexit pcsp_remove(struct platform_device *dev)
 
 static void pcsp_stop_beep(struct snd_pcsp *chip)
 {
-       spin_lock_irq(&chip->substream_lock);
-       if (!chip->playback_substream)
-               pcspkr_stop_sound();
-       spin_unlock_irq(&chip->substream_lock);
+       pcsp_sync_stop(chip);
+       pcspkr_stop_sound();
 }
 
 #ifdef CONFIG_PM
index 1d661f7..cdef266 100644 (file)
@@ -62,6 +62,8 @@ struct snd_pcsp {
        unsigned short port, irq, dma;
        spinlock_t substream_lock;
        struct snd_pcm_substream *playback_substream;
+       unsigned int fmt_size;
+       unsigned int is_signed;
        size_t playback_ptr;
        size_t period_ptr;
        atomic_t timer_active;
@@ -77,6 +79,7 @@ struct snd_pcsp {
 extern struct snd_pcsp pcsp_chip;
 
 extern enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle);
+extern void pcsp_sync_stop(struct snd_pcsp *chip);
 
 extern int snd_pcsp_new_pcm(struct snd_pcsp *chip);
 extern int snd_pcsp_new_mixer(struct snd_pcsp *chip);
index 1f42e40..84cc265 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/interrupt.h>
 #include <sound/pcm.h>
 #include <asm/io.h>
 #include "pcsp.h"
@@ -19,61 +20,57 @@ MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround "
 
 #define DMIX_WANTS_S16 1
 
-enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
+/*
+ * Call snd_pcm_period_elapsed in a tasklet
+ * This avoids spinlock messes and long-running irq contexts
+ */
+static void pcsp_call_pcm_elapsed(unsigned long priv)
+{
+       if (atomic_read(&pcsp_chip.timer_active)) {
+               struct snd_pcm_substream *substream;
+               substream = pcsp_chip.playback_substream;
+               if (substream)
+                       snd_pcm_period_elapsed(substream);
+       }
+}
+
+static DECLARE_TASKLET(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed, 0);
+
+/* write the port and returns the next expire time in ns;
+ * called at the trigger-start and in hrtimer callback
+ */
+static unsigned long pcsp_timer_update(struct hrtimer *handle)
 {
        unsigned char timer_cnt, val;
-       int fmt_size, periods_elapsed;
        u64 ns;
-       size_t period_bytes, buffer_bytes;
        struct snd_pcm_substream *substream;
        struct snd_pcm_runtime *runtime;
        struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer);
+       unsigned long flags;
 
        if (chip->thalf) {
                outb(chip->val61, 0x61);
                chip->thalf = 0;
                if (!atomic_read(&chip->timer_active))
-                       return HRTIMER_NORESTART;
-               hrtimer_forward(&chip->timer, hrtimer_get_expires(&chip->timer),
-                               ktime_set(0, chip->ns_rem));
-               return HRTIMER_RESTART;
+                       return 0;
+               return chip->ns_rem;
        }
 
-       spin_lock_irq(&chip->substream_lock);
-       /* Takashi Iwai says regarding this extra lock:
-
-       If the irq handler handles some data on the DMA buffer, it should
-       do snd_pcm_stream_lock().
-       That protects basically against all races among PCM callbacks, yes.
-       However, there are two remaining issues:
-       1. The substream pointer you try to lock isn't protected _before_
-         this lock yet.
-       2. snd_pcm_period_elapsed() itself acquires the lock.
-       The requirement of another lock is because of 1.  When you get
-       chip->playback_substream, it's not protected.
-       Keeping this lock while snd_pcm_period_elapsed() assures the substream
-       is still protected (at least, not released).  And the other status is
-       handled properly inside snd_pcm_stream_lock() in
-       snd_pcm_period_elapsed().
-
-       */
-       if (!chip->playback_substream)
-               goto exit_nr_unlock1;
-       substream = chip->playback_substream;
-       snd_pcm_stream_lock(substream);
        if (!atomic_read(&chip->timer_active))
-               goto exit_nr_unlock2;
+               return 0;
+       substream = chip->playback_substream;
+       if (!substream)
+               return 0;
 
        runtime = substream->runtime;
-       fmt_size = snd_pcm_format_physical_width(runtime->format) >> 3;
        /* assume it is mono! */
-       val = runtime->dma_area[chip->playback_ptr + fmt_size - 1];
-       if (snd_pcm_format_signed(runtime->format))
+       val = runtime->dma_area[chip->playback_ptr + chip->fmt_size - 1];
+       if (chip->is_signed)
                val ^= 0x80;
        timer_cnt = val * CUR_DIV() / 256;
 
        if (timer_cnt && chip->enable) {
-               spin_lock(&i8253_lock);
+               spin_lock_irqsave(&i8253_lock, flags);
                if (!nforce_wa) {
                        outb_p(chip->val61, 0x61);
                        outb_p(timer_cnt, 0x42);
@@ -82,12 +79,39 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
                        outb(chip->val61 ^ 2, 0x61);
                        chip->thalf = 1;
                }
-               spin_unlock(&i8253_lock);
+               spin_unlock_irqrestore(&i8253_lock, flags);
        }
 
+       chip->ns_rem = PCSP_PERIOD_NS();
+       ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem);
+       chip->ns_rem -= ns;
+       return ns;
+}
+
+enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
+{
+       struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer);
+       struct snd_pcm_substream *substream;
+       int periods_elapsed, pointer_update;
+       size_t period_bytes, buffer_bytes;
+       unsigned long ns;
+       unsigned long flags;
+
+       pointer_update = !chip->thalf;
+       ns = pcsp_timer_update(handle);
+       if (!ns)
+               return HRTIMER_NORESTART;
+
+       /* update the playback position */
+       substream = chip->playback_substream;
+       if (!substream)
+               return HRTIMER_NORESTART;
+
        period_bytes = snd_pcm_lib_period_bytes(substream);
        buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
-       chip->playback_ptr += PCSP_INDEX_INC() * fmt_size;
+
+       spin_lock_irqsave(&chip->substream_lock, flags);
+       chip->playback_ptr += PCSP_INDEX_INC() * chip->fmt_size;
        periods_elapsed = chip->playback_ptr - chip->period_ptr;
        if (periods_elapsed < 0) {
 #if PCSP_DEBUG
@@ -102,41 +126,30 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
         * or ALSA will BUG on us. */
        chip->playback_ptr %= buffer_bytes;
 
-       snd_pcm_stream_unlock(substream);
-
        if (periods_elapsed) {
-               snd_pcm_period_elapsed(substream);
                chip->period_ptr += periods_elapsed * period_bytes;
                chip->period_ptr %= buffer_bytes;
        }
+       spin_unlock_irqrestore(&chip->substream_lock, flags);
 
-       spin_unlock_irq(&chip->substream_lock);
+       if (periods_elapsed)
+               tasklet_schedule(&pcsp_pcm_tasklet);
 
-       if (!atomic_read(&chip->timer_active))
-               return HRTIMER_NORESTART;
+       hrtimer_forward(handle, hrtimer_get_expires(handle), ns_to_ktime(ns));
 
-       chip->ns_rem = PCSP_PERIOD_NS();
-       ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem);
-       chip->ns_rem -= ns;
-       hrtimer_forward(&chip->timer, hrtimer_get_expires(&chip->timer),
-                                                       ktime_set(0, ns));
        return HRTIMER_RESTART;
-
-exit_nr_unlock2:
-       snd_pcm_stream_unlock(substream);
-exit_nr_unlock1:
-       spin_unlock_irq(&chip->substream_lock);
-       return HRTIMER_NORESTART;
 }
 
-static void pcsp_start_playing(struct snd_pcsp *chip)
+static int pcsp_start_playing(struct snd_pcsp *chip)
 {
+       unsigned long ns;
+
 #if PCSP_DEBUG
        printk(KERN_INFO "PCSP: start_playing called\n");
 #endif
        if (atomic_read(&chip->timer_active)) {
                printk(KERN_ERR "PCSP: Timer already active\n");
-               return;
+               return -EIO;
        }
 
        spin_lock(&i8253_lock);
@@ -146,7 +159,12 @@ static void pcsp_start_playing(struct snd_pcsp *chip)
        atomic_set(&chip->timer_active, 1);
        chip->thalf = 0;
 
-       hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+       ns = pcsp_timer_update(&pcsp_chip.timer);
+       if (!ns)
+               return -EIO;
+
+       hrtimer_start(&pcsp_chip.timer, ktime_set(0, ns), HRTIMER_MODE_REL);
+       return 0;
 }
 
 static void pcsp_stop_playing(struct snd_pcsp *chip)
@@ -165,26 +183,35 @@ static void pcsp_stop_playing(struct snd_pcsp *chip)
        spin_unlock(&i8253_lock);
 }
 
+/*
+ * Force to stop and sync the stream
+ */
+void pcsp_sync_stop(struct snd_pcsp *chip)
+{
+       local_irq_disable();
+       pcsp_stop_playing(chip);
+       local_irq_enable();
+       hrtimer_cancel(&chip->timer);
+       tasklet_kill(&pcsp_pcm_tasklet);
+}
+
 static int snd_pcsp_playback_close(struct snd_pcm_substream *substream)
 {
        struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
 #if PCSP_DEBUG
        printk(KERN_INFO "PCSP: close called\n");
 #endif
-       if (atomic_read(&chip->timer_active)) {
-               printk(KERN_ERR "PCSP: timer still active\n");
-               pcsp_stop_playing(chip);
-       }
-       spin_lock_irq(&chip->substream_lock);
+       pcsp_sync_stop(chip);
        chip->playback_substream = NULL;
-       spin_unlock_irq(&chip->substream_lock);
        return 0;
 }
 
 static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,
                                       struct snd_pcm_hw_params *hw_params)
 {
+       struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
        int err;
+       pcsp_sync_stop(chip);
        err = snd_pcm_lib_malloc_pages(substream,
                                      params_buffer_bytes(hw_params));
        if (err < 0)
@@ -194,9 +221,11 @@ static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,
 
 static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream)
 {
+       struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
 #if PCSP_DEBUG
        printk(KERN_INFO "PCSP: hw_free called\n");
 #endif
+       pcsp_sync_stop(chip);
        return snd_pcm_lib_free_pages(substream);
 }
 
@@ -212,8 +241,12 @@ static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream)
                        snd_pcm_lib_period_bytes(substream),
                        substream->runtime->periods);
 #endif
+       pcsp_sync_stop(chip);
        chip->playback_ptr = 0;
        chip->period_ptr = 0;
+       chip->fmt_size =
+               snd_pcm_format_physical_width(substream->runtime->format) >> 3;
+       chip->is_signed = snd_pcm_format_signed(substream->runtime->format);
        return 0;
 }
 
@@ -226,8 +259,7 @@ static int snd_pcsp_trigger(struct snd_pcm_substream *substream, int cmd)
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
-               pcsp_start_playing(chip);
-               break;
+               return pcsp_start_playing(chip);
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
                pcsp_stop_playing(chip);
@@ -242,7 +274,11 @@ static snd_pcm_uframes_t snd_pcsp_playback_pointer(struct snd_pcm_substream
                                                   *substream)
 {
        struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
-       return bytes_to_frames(substream->runtime, chip->playback_ptr);
+       unsigned int pos;
+       spin_lock(&chip->substream_lock);
+       pos = chip->playback_ptr;
+       spin_unlock(&chip->substream_lock);
+       return bytes_to_frames(substream->runtime, pos);
 }
 
 static struct snd_pcm_hardware snd_pcsp_playback = {
@@ -279,9 +315,7 @@ static int snd_pcsp_playback_open(struct snd_pcm_substream *substream)
                return -EBUSY;
        }
        runtime->hw = snd_pcsp_playback;
-       spin_lock_irq(&chip->substream_lock);
        chip->playback_substream = substream;
-       spin_unlock_irq(&chip->substream_lock);
        return 0;
 }
 
index 473b07f..14e3354 100644 (file)
@@ -548,7 +548,7 @@ irqreturn_t snd_vx_irq_handler(int irq, void *dev)
            (chip->chip_status & VX_STAT_IS_STALE))
                return IRQ_NONE;
        if (! vx_test_and_ack(chip))
-               tasklet_hi_schedule(&chip->tq);
+               tasklet_schedule(&chip->tq);
        return IRQ_HANDLED;
 }
 
index 27de574..6644d00 100644 (file)
@@ -823,7 +823,7 @@ static int vx_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
                 * we trigger the pipe using tasklet, so that the interrupts are
                 * issued surely after the trigger is completed.
                 */ 
-               tasklet_hi_schedule(&pipe->start_tq);
+               tasklet_schedule(&pipe->start_tq);
                chip->pcm_running++;
                pipe->running = 1;
                break;
index 667eccc..ea06877 100644 (file)
@@ -140,8 +140,10 @@ static int __devinit snd_sb8_probe(struct device *pdev, unsigned int dev)
                                break;
                        }
                }
-               if (i >= ARRAY_SIZE(possible_ports))
+               if (i >= ARRAY_SIZE(possible_ports)) {
+                       err = -EINVAL;
                        goto _err;
+               }
        }
        acard->chip = chip;
                        
index 7003711..6e3a184 100644 (file)
@@ -208,7 +208,8 @@ config SND_OXYGEN
           * AuzenTech X-Meridian
           * Bgears b-Enspirer
           * Club3D Theatron DTS
-          * HT-Omega Claro
+          * HT-Omega Claro (plus)
+          * HT-Omega Claro halo (XT)
           * Razer Barracuda AC-1
           * Sondigo Inferno
 
@@ -497,129 +498,7 @@ config SND_FM801_TEA575X
        depends on SND_FM801_TEA575X_BOOL
        default SND_FM801
 
-config SND_HDA_INTEL
-       tristate "Intel HD Audio"
-       select SND_PCM
-       select SND_VMASTER
-       help
-         Say Y here to include support for Intel "High Definition
-         Audio" (Azalia) motherboard devices.
-
-         To compile this driver as a module, choose M here: the module
-         will be called snd-hda-intel.
-
-config SND_HDA_HWDEP
-       bool "Build hwdep interface for HD-audio driver"
-       depends on SND_HDA_INTEL
-       select SND_HWDEP
-       help
-         Say Y here to build a hwdep interface for HD-audio driver.
-         This interface can be used for out-of-band communication
-         with codecs for debugging purposes.
-
-config SND_HDA_INPUT_BEEP
-       bool "Support digital beep via input layer"
-       depends on SND_HDA_INTEL
-       depends on INPUT=y || INPUT=SND_HDA_INTEL
-       help
-         Say Y here to build a digital beep interface for HD-audio
-         driver. This interface is used to generate digital beeps.
-
-config SND_HDA_CODEC_REALTEK
-       bool "Build Realtek HD-audio codec support"
-       depends on SND_HDA_INTEL
-       default y
-       help
-         Say Y here to include Realtek HD-audio codec support in
-         snd-hda-intel driver, such as ALC880.
-
-config SND_HDA_CODEC_ANALOG
-       bool "Build Analog Device HD-audio codec support"
-       depends on SND_HDA_INTEL
-       default y
-       help
-         Say Y here to include Analog Device HD-audio codec support in
-         snd-hda-intel driver, such as AD1986A.
-
-config SND_HDA_CODEC_SIGMATEL
-       bool "Build IDT/Sigmatel HD-audio codec support"
-       depends on SND_HDA_INTEL
-       default y
-       help
-         Say Y here to include IDT (Sigmatel) HD-audio codec support in
-         snd-hda-intel driver, such as STAC9200.
-
-config SND_HDA_CODEC_VIA
-       bool "Build VIA HD-audio codec support"
-       depends on SND_HDA_INTEL
-       default y
-       help
-         Say Y here to include VIA HD-audio codec support in
-         snd-hda-intel driver, such as VT1708.
-
-config SND_HDA_CODEC_ATIHDMI
-       bool "Build ATI HDMI HD-audio codec support"
-       depends on SND_HDA_INTEL
-       default y
-       help
-         Say Y here to include ATI HDMI HD-audio codec support in
-         snd-hda-intel driver, such as ATI RS600 HDMI.
-
-config SND_HDA_CODEC_NVHDMI
-       bool "Build NVIDIA HDMI HD-audio codec support"
-       depends on SND_HDA_INTEL
-       default y
-       help
-         Say Y here to include NVIDIA HDMI HD-audio codec support in
-         snd-hda-intel driver, such as NVIDIA MCP78 HDMI.
-
-config SND_HDA_CODEC_CONEXANT
-       bool "Build Conexant HD-audio codec support"
-       depends on SND_HDA_INTEL
-       default y
-       help
-         Say Y here to include Conexant HD-audio codec support in
-         snd-hda-intel driver, such as CX20549.
-
-config SND_HDA_CODEC_CMEDIA
-       bool "Build C-Media HD-audio codec support"
-       depends on SND_HDA_INTEL
-       default y
-       help
-         Say Y here to include C-Media HD-audio codec support in
-         snd-hda-intel driver, such as CMI9880.
-
-config SND_HDA_CODEC_SI3054
-       bool "Build Silicon Labs 3054 HD-modem codec support"
-       depends on SND_HDA_INTEL
-       default y
-       help
-         Say Y here to include Silicon Labs 3054 HD-modem codec
-         (and compatibles) support in snd-hda-intel driver.
-
-config SND_HDA_GENERIC
-       bool "Enable generic HD-audio codec parser"
-       depends on SND_HDA_INTEL
-       default y
-       help
-         Say Y here to enable the generic HD-audio codec parser
-         in snd-hda-intel driver.
-
-config SND_HDA_POWER_SAVE
-       bool "Aggressive power-saving on HD-audio"
-       depends on SND_HDA_INTEL && EXPERIMENTAL
-       help
-         Say Y here to enable more aggressive power-saving mode on
-         HD-audio driver.  The power-saving timeout can be configured
-         via power_save option or over sysfs on-the-fly.
-
-config SND_HDA_POWER_SAVE_DEFAULT
-       int "Default time-out for HD-audio power-save mode"
-       depends on SND_HDA_POWER_SAVE
-       default 0
-       help
-         The default time-out value in seconds for HD-audio automatic
-         power-save mode.  0 means to disable the power-save mode.
+source "sound/pci/hda/Kconfig"
 
 config SND_HDSP
        tristate "RME Hammerfall DSP Audio"
index bd510ec..e2b843b 100644 (file)
@@ -175,7 +175,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
 { 0x574d4C04, 0xffffffff, "WM9704M,WM9704Q",   patch_wolfson04, NULL},
 { 0x574d4C05, 0xffffffff, "WM9705,WM9710",     patch_wolfson05, NULL},
 { 0x574d4C09, 0xffffffff, "WM9709",            NULL,           NULL},
-{ 0x574d4C12, 0xffffffff, "WM9711,WM9712",     patch_wolfson11, NULL},
+{ 0x574d4C12, 0xffffffff, "WM9711,WM9712,WM9715",      patch_wolfson11, NULL},
 { 0x574d4c13, 0xffffffff, "WM9713,WM9714",     patch_wolfson13, NULL, AC97_DEFAULT_POWER_OFF},
 { 0x594d4800, 0xffffffff, "YMF743",            patch_yamaha_ymf743,    NULL },
 { 0x594d4802, 0xffffffff, "YMF752",            NULL,           NULL },
index 6e831af..81bc93e 100644 (file)
@@ -2054,8 +2054,9 @@ static const struct snd_kcontrol_new snd_ac97_ad1888_controls[] = {
                .get = snd_ac97_ad1888_lohpsel_get,
                .put = snd_ac97_ad1888_lohpsel_put
        },
-       AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, 2, 1, 1),
-       AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, 12, 1, 1),
+       AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, AC97_AD_VREFD_SHIFT, 1, 1),
+       AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2,
+                       AC97_AD_HPFD_SHIFT, 1, 1),
        AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0),
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -2832,6 +2833,8 @@ static int patch_alc655(struct snd_ac97 * ac97)
                        val &= ~(1 << 1); /* Pin 47 is EAPD (for internal speaker) */
                else
                        val |= (1 << 1); /* Pin 47 is spdif input pin */
+               /* this seems missing on some hardwares */
+               ac97->ext_id |= AC97_EI_SPDIF;
        }
        val &= ~(1 << 12); /* vref enable */
        snd_ac97_write_cache(ac97, 0x7a, val);
index 74175fc..14b8d9a 100644 (file)
@@ -664,10 +664,14 @@ struct snd_ca0106_pcm {
 struct snd_ca0106_details {
         u32 serial;
         char * name;
-        int ac97;
-       int gpio_type;
-       int i2c_adc;
-       int spi_dac;
+       int ac97;       /* ac97 = 0 -> Select MIC, Line in, TAD in, AUX in.
+                          ac97 = 1 -> Default to AC97 in. */
+       int gpio_type;  /* gpio_type = 1 -> shared mic-in/line-in
+                          gpio_type = 2 -> shared side-out/line-in. */
+       int i2c_adc;    /* with i2c_adc=1, the driver adds some capture volume
+                          controls, phone, mic, line-in and aux. */
+       int spi_dac;    /* spi_dac=1 adds the mute switch for each analog
+                          output, front, rear, etc. */
 };
 
 // definition of the chip-specific record
@@ -686,11 +690,12 @@ struct snd_ca0106 {
        spinlock_t emu_lock;
 
        struct snd_ac97 *ac97;
-       struct snd_pcm *pcm;
+       struct snd_pcm *pcm[4];
 
        struct snd_ca0106_channel playback_channels[4];
        struct snd_ca0106_channel capture_channels[4];
-       u32 spdif_bits[4];             /* s/pdif out setup */
+       u32 spdif_bits[4];             /* s/pdif out default setup */
+       u32 spdif_str_bits[4];         /* s/pdif out per-stream setup */
        int spdif_enable;
        int capture_source;
        int i2c_capture_source;
@@ -703,6 +708,11 @@ struct snd_ca0106 {
        struct snd_ca_midi midi2;
 
        u16 spi_dac_reg[16];
+
+#ifdef CONFIG_PM
+#define NUM_SAVED_VOLUMES      9
+       unsigned int saved_vol[NUM_SAVED_VOLUMES];
+#endif
 };
 
 int snd_ca0106_mixer(struct snd_ca0106 *emu);
@@ -721,3 +731,11 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu, u32 reg, u32 value);
 
 int snd_ca0106_spi_write(struct snd_ca0106 * emu,
                                   unsigned int data);
+
+#ifdef CONFIG_PM
+void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip);
+void snd_ca0106_mixer_resume(struct snd_ca0106 *chip);
+#else
+#define snd_ca0106_mixer_suspend(chip) do { } while (0)
+#define snd_ca0106_mixer_resume(chip)  do { } while (0)
+#endif
index 88fbf28..0e62205 100644 (file)
@@ -254,7 +254,7 @@ static struct snd_ca0106_details ca0106_chip_details[] = {
           .name   = "MSI K8N Diamond MB",
           .gpio_type = 2,
           .i2c_adc = 1,
-          .spi_dac = 2 } ,
+          .spi_dac = 1 } ,
         /* Shuttle XPC SD31P which has an onboard Creative Labs
          * Sound Blaster Live! 24-bit EAX
          * high-definition 7.1 audio processor".
@@ -305,9 +305,15 @@ static struct snd_pcm_hardware snd_ca0106_capture_hw = {
                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
                                 SNDRV_PCM_INFO_MMAP_VALID),
        .formats =              SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+#if 0 /* FIXME: looks like 44.1kHz capture causes noisy output on 48kHz */
        .rates =                (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
                                 SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000),
        .rate_min =             44100,
+#else
+       .rates =                (SNDRV_PCM_RATE_48000 |
+                                SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000),
+       .rate_min =             48000,
+#endif /* FIXME */
        .rate_max =             192000,
        .channels_min =         2,
        .channels_max =         2,
@@ -479,6 +485,15 @@ static const int spi_dacd_bit[] = {
        [PCM_UNKNOWN_CHANNEL]   = SPI_DACD1_BIT,
 };
 
+static void restore_spdif_bits(struct snd_ca0106 *chip, int idx)
+{
+       if (chip->spdif_str_bits[idx] != chip->spdif_bits[idx]) {
+               chip->spdif_str_bits[idx] = chip->spdif_bits[idx];
+               snd_ca0106_ptr_write(chip, SPCS0 + idx, 0,
+                                    chip->spdif_str_bits[idx]);
+       }
+}
+
 /* open_playback callback */
 static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream,
                                                int channel_id)
@@ -524,6 +539,9 @@ static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substr
                if (err < 0)
                        return err;
        }
+
+       restore_spdif_bits(chip, channel_id);
+
        return 0;
 }
 
@@ -535,6 +553,8 @@ static int snd_ca0106_pcm_close_playback(struct snd_pcm_substream *substream)
         struct snd_ca0106_pcm *epcm = runtime->private_data;
        chip->playback_channels[epcm->channel_id].use = 0;
 
+       restore_spdif_bits(chip, epcm->channel_id);
+
        if (chip->details->spi_dac && epcm->channel_id != PCM_FRONT_CHANNEL) {
                const int reg = spi_dacd_reg[epcm->channel_id];
 
@@ -847,15 +867,18 @@ static int snd_ca0106_pcm_trigger_playback(struct snd_pcm_substream *substream,
         struct snd_pcm_substream *s;
        u32 basic = 0;
        u32 extended = 0;
-       int running=0;
+       u32 bits;
+       int running = 0;
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
-               running=1;
+       case SNDRV_PCM_TRIGGER_RESUME:
+               running = 1;
                break;
        case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
        default:
-               running=0;
+               running = 0;
                break;
        }
         snd_pcm_group_for_each_entry(s, substream) {
@@ -865,22 +888,32 @@ static int snd_ca0106_pcm_trigger_playback(struct snd_pcm_substream *substream,
                runtime = s->runtime;
                epcm = runtime->private_data;
                channel = epcm->channel_id;
-               //snd_printk("channel=%d\n",channel);
+               /* snd_printk("channel=%d\n",channel); */
                epcm->running = running;
-               basic |= (0x1<<channel);
-               extended |= (0x10<<channel);
+               basic |= (0x1 << channel);
+               extended |= (0x10 << channel);
                 snd_pcm_trigger_done(s, substream);
         }
-       //snd_printk("basic=0x%x, extended=0x%x\n",basic, extended);
+       /* snd_printk("basic=0x%x, extended=0x%x\n",basic, extended); */
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
-               snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) | (extended));
-               snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0)|(basic));
+       case SNDRV_PCM_TRIGGER_RESUME:
+               bits = snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0);
+               bits |= extended;
+               snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, bits);
+               bits = snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0);
+               bits |= basic;
+               snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, bits);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
-               snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(basic));
-               snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(extended));
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               bits = snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0);
+               bits &= ~basic;
+               snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, bits);
+               bits = snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0);
+               bits &= ~extended;
+               snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, bits);
                break;
        default:
                result = -EINVAL;
@@ -1103,21 +1136,13 @@ static int snd_ca0106_ac97(struct snd_ca0106 *chip)
        return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
 }
 
+static void ca0106_stop_chip(struct snd_ca0106 *chip);
+
 static int snd_ca0106_free(struct snd_ca0106 *chip)
 {
-       if (chip->res_port != NULL) {    /* avoid access to already used hardware */
-               // disable interrupts
-               snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
-               outl(0, chip->port + INTE);
-               snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
-               udelay(1000);
-               // disable audio
-               //outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG);
-               outl(0, chip->port + HCFG);
-               /* FIXME: We need to stop and DMA transfers here.
-                *        But as I am not sure how yet, we cannot from the dma pages.
-                * So we can fix: snd-malloc: Memory leak?  pages not freed = 8
-                */
+       if (chip->res_port != NULL) {
+               /* avoid access to already used hardware */
+               ca0106_stop_chip(chip);
        }
        if (chip->irq >= 0)
                free_irq(chip->irq, chip);
@@ -1203,15 +1228,14 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct snd_pcm **rpcm)
+static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
 {
        struct snd_pcm *pcm;
        struct snd_pcm_substream *substream;
        int err;
   
-       if (rpcm)
-               *rpcm = NULL;
-       if ((err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm)) < 0)
+       err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm);
+       if (err < 0)
                return err;
   
        pcm->private_data = emu;
@@ -1238,7 +1262,6 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct s
        pcm->info_flags = 0;
        pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
        strcpy(pcm->name, "CA0106");
-       emu->pcm = pcm;
 
        for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 
            substream; 
@@ -1260,8 +1283,7 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct s
                        return err;
        }
   
-       if (rpcm)
-               *rpcm = pcm;
+       emu->pcm[device] = pcm;
   
        return 0;
 }
@@ -1301,89 +1323,10 @@ static unsigned int i2c_adc_init[][2] = {
        { 0x15, ADC_MUX_LINEIN },  /* ADC Mixer control */
 };
 
-static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
-                                        struct pci_dev *pci,
-                                        struct snd_ca0106 **rchip)
+static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
 {
-       struct snd_ca0106 *chip;
-       struct snd_ca0106_details *c;
-       int err;
        int ch;
-       static struct snd_device_ops ops = {
-               .dev_free = snd_ca0106_dev_free,
-       };
-  
-       *rchip = NULL;
-  
-       if ((err = pci_enable_device(pci)) < 0)
-               return err;
-       if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 ||
-           pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) {
-               printk(KERN_ERR "error to set 32bit mask DMA\n");
-               pci_disable_device(pci);
-               return -ENXIO;
-       }
-  
-       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
-       if (chip == NULL) {
-               pci_disable_device(pci);
-               return -ENOMEM;
-       }
-  
-       chip->card = card;
-       chip->pci = pci;
-       chip->irq = -1;
-
-       spin_lock_init(&chip->emu_lock);
-  
-       chip->port = pci_resource_start(pci, 0);
-       if ((chip->res_port = request_region(chip->port, 0x20,
-                                            "snd_ca0106")) == NULL) { 
-               snd_ca0106_free(chip);
-               printk(KERN_ERR "cannot allocate the port\n");
-               return -EBUSY;
-       }
-
-       if (request_irq(pci->irq, snd_ca0106_interrupt,
-                       IRQF_SHARED, "snd_ca0106", chip)) {
-               snd_ca0106_free(chip);
-               printk(KERN_ERR "cannot grab irq\n");
-               return -EBUSY;
-       }
-       chip->irq = pci->irq;
-  
-       /* This stores the periods table. */ 
-       if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), 1024, &chip->buffer) < 0) {
-               snd_ca0106_free(chip);
-               return -ENOMEM;
-       }
-
-       pci_set_master(pci);
-       /* read serial */
-       pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
-       pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
-#if 1
-       printk(KERN_INFO "snd-ca0106: Model %04x Rev %08x Serial %08x\n", chip->model,
-              pci->revision, chip->serial);
-#endif
-       strcpy(card->driver, "CA0106");
-       strcpy(card->shortname, "CA0106");
-
-       for (c = ca0106_chip_details; c->serial; c++) {
-               if (subsystem[dev]) {
-                       if (c->serial == subsystem[dev])
-                               break;
-               } else if (c->serial == chip->serial)
-                       break;
-       }
-       chip->details = c;
-       if (subsystem[dev]) {
-               printk(KERN_INFO "snd-ca0106: Sound card name=%s, subsystem=0x%x. Forced to subsystem=0x%x\n",
-                        c->name, chip->serial, subsystem[dev]);
-       }
-
-       sprintf(card->longname, "%s at 0x%lx irq %i",
-               c->name, chip->port, chip->irq);
+       unsigned int def_bits;
 
        outl(0, chip->port + INTE);
 
@@ -1401,31 +1344,22 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
         *  AN                = 0     (Audio data)
         *  P                 = 0     (Consumer)
         */
-       snd_ca0106_ptr_write(chip, SPCS0, 0,
-                               chip->spdif_bits[0] =
-                               SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
-                               SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
-                               SPCS_GENERATIONSTATUS | 0x00001200 |
-                               0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+       def_bits =
+               SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+               SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+               SPCS_GENERATIONSTATUS | 0x00001200 |
+               0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
+       if (!resume) {
+               chip->spdif_str_bits[0] = chip->spdif_bits[0] = def_bits;
+               chip->spdif_str_bits[1] = chip->spdif_bits[1] = def_bits;
+               chip->spdif_str_bits[2] = chip->spdif_bits[2] = def_bits;
+               chip->spdif_str_bits[3] = chip->spdif_bits[3] = def_bits;
+       }
        /* Only SPCS1 has been tested */
-       snd_ca0106_ptr_write(chip, SPCS1, 0,
-                               chip->spdif_bits[1] =
-                               SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
-                               SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
-                               SPCS_GENERATIONSTATUS | 0x00001200 |
-                               0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
-       snd_ca0106_ptr_write(chip, SPCS2, 0,
-                               chip->spdif_bits[2] =
-                               SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
-                               SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
-                               SPCS_GENERATIONSTATUS | 0x00001200 |
-                               0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
-       snd_ca0106_ptr_write(chip, SPCS3, 0,
-                               chip->spdif_bits[3] =
-                               SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
-                               SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
-                               SPCS_GENERATIONSTATUS | 0x00001200 |
-                               0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+       snd_ca0106_ptr_write(chip, SPCS1, 0, chip->spdif_str_bits[1]);
+       snd_ca0106_ptr_write(chip, SPCS0, 0, chip->spdif_str_bits[0]);
+       snd_ca0106_ptr_write(chip, SPCS2, 0, chip->spdif_str_bits[2]);
+       snd_ca0106_ptr_write(chip, SPCS3, 0, chip->spdif_str_bits[3]);
 
         snd_ca0106_ptr_write(chip, PLAYBACK_MUTE, 0, 0x00fc0000);
         snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000);
@@ -1433,92 +1367,124 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
         /* Write 0x8000 to AC97_REC_GAIN to mute it. */
         outb(AC97_REC_GAIN, chip->port + AC97ADDRESS);
         outw(0x8000, chip->port + AC97DATA);
-#if 0
+#if 0 /* FIXME: what are these? */
        snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006);
        snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006);
        snd_ca0106_ptr_write(chip, 0x43, 0, 0x2108006);
        snd_ca0106_ptr_write(chip, 0x44, 0, 0x2108006);
 #endif
 
-       //snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); /* OSS drivers set this. */
+       /* OSS drivers set this. */
+       /* snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); */
+
        /* Analog or Digital output */
        snd_ca0106_ptr_write(chip, SPDIF_SELECT1, 0, 0xf);
-       snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers. Use 0x000f0000 for surround71 */
+       /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers.
+        * Use 0x000f0000 for surround71
+        */
+       snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000);
+
        chip->spdif_enable = 0; /* Set digital SPDIF output off */
-       //snd_ca0106_ptr_write(chip, 0x45, 0, 0); /* Analogue out */
-       //snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00); /* Digital out */
+       /*snd_ca0106_ptr_write(chip, 0x45, 0, 0);*/ /* Analogue out */
+       /*snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00);*/ /* Digital out */
+
+       /* goes to 0x40c80000 when doing SPDIF IN/OUT */
+       snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 0, 0x40c81000);
+       /* (Mute) CAPTURE feedback into PLAYBACK volume.
+        * Only lower 16 bits matter.
+        */
+       snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 1, 0xffffffff);
+       /* SPDIF IN Volume */
+       snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 2, 0x30300000);
+       /* SPDIF IN Volume, 0x70 = (vol & 0x3f) | 0x40 */
+       snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 3, 0x00700000);
 
-       snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 0, 0x40c81000); /* goes to 0x40c80000 when doing SPDIF IN/OUT */
-       snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 1, 0xffffffff); /* (Mute) CAPTURE feedback into PLAYBACK volume. Only lower 16 bits matter. */
-       snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 2, 0x30300000); /* SPDIF IN Volume */
-       snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 3, 0x00700000); /* SPDIF IN Volume, 0x70 = (vol & 0x3f) | 0x40 */
        snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING1, 0, 0x32765410);
        snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING2, 0, 0x76767676);
        snd_ca0106_ptr_write(chip, CAPTURE_ROUTING1, 0, 0x32765410);
        snd_ca0106_ptr_write(chip, CAPTURE_ROUTING2, 0, 0x76767676);
-       for(ch = 0; ch < 4; ch++) {
-               snd_ca0106_ptr_write(chip, CAPTURE_VOLUME1, ch, 0x30303030); /* Only high 16 bits matter */
+
+       for (ch = 0; ch < 4; ch++) {
+               /* Only high 16 bits matter */
+               snd_ca0106_ptr_write(chip, CAPTURE_VOLUME1, ch, 0x30303030);
                snd_ca0106_ptr_write(chip, CAPTURE_VOLUME2, ch, 0x30303030);
-               //snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0x40404040); /* Mute */
-               //snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0x40404040); /* Mute */
-               snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff); /* Mute */
-               snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff); /* Mute */
+#if 0 /* Mute */
+               snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0x40404040);
+               snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0x40404040);
+               snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff);
+               snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff);
+#endif
        }
        if (chip->details->i2c_adc == 1) {
                /* Select MIC, Line in, TAD in, AUX in */
                snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4);
                /* Default to CAPTURE_SOURCE to i2s in */
-               chip->capture_source = 3;
+               if (!resume)
+                       chip->capture_source = 3;
        } else if (chip->details->ac97 == 1) {
                /* Default to AC97 in */
                snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x444400e4);
                /* Default to CAPTURE_SOURCE to AC97 in */
-               chip->capture_source = 4;
+               if (!resume)
+                       chip->capture_source = 4;
        } else {
                /* Select MIC, Line in, TAD in, AUX in */
                snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4);
                /* Default to Set CAPTURE_SOURCE to i2s in */
-               chip->capture_source = 3;
+               if (!resume)
+                       chip->capture_source = 3;
        }
 
-        if (chip->details->gpio_type == 2) { /* The SB0438 use GPIO differently. */
-               /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
+       if (chip->details->gpio_type == 2) {
+               /* The SB0438 use GPIO differently. */
+               /* FIXME: Still need to find out what the other GPIO bits do.
+                * E.g. For digital spdif out.
+                */
                outl(0x0, chip->port+GPIO);
-               //outl(0x00f0e000, chip->port+GPIO); /* Analog */
+               /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */
                outl(0x005f5301, chip->port+GPIO); /* Analog */
-       } else if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */
-               /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
+       } else if (chip->details->gpio_type == 1) {
+               /* The SB0410 and SB0413 use GPIO differently. */
+               /* FIXME: Still need to find out what the other GPIO bits do.
+                * E.g. For digital spdif out.
+                */
                outl(0x0, chip->port+GPIO);
-               //outl(0x00f0e000, chip->port+GPIO); /* Analog */
+               /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */
                outl(0x005f5301, chip->port+GPIO); /* Analog */
        } else {
                outl(0x0, chip->port+GPIO);
                outl(0x005f03a3, chip->port+GPIO); /* Analog */
-               //outl(0x005f02a2, chip->port+GPIO);   /* SPDIF */
+               /* outl(0x005f02a2, chip->port+GPIO); */ /* SPDIF */
        }
        snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */
 
-       //outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG);
-       //outl(0x00001409, chip->port+HCFG); /* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
-       //outl(0x00000009, chip->port+HCFG);
-       outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); /* AC97 2.0, Enable outputs. */
+       /* outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG); */
+       /* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
+       /* outl(0x00001409, chip->port+HCFG); */
+       /* outl(0x00000009, chip->port+HCFG); */
+       /* AC97 2.0, Enable outputs. */
+       outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG);
 
-        if (chip->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */
+       if (chip->details->i2c_adc == 1) {
+               /* The SB0410 and SB0413 use I2C to control ADC. */
                int size, n;
 
                size = ARRAY_SIZE(i2c_adc_init);
-                //snd_printk("I2C:array size=0x%x\n", size);
-               for (n=0; n < size; n++) {
-                       snd_ca0106_i2c_write(chip, i2c_adc_init[n][0], i2c_adc_init[n][1]);
-               }
-               for (n=0; n < 4; n++) {
-                       chip->i2c_capture_volume[n][0]= 0xcf;
-                       chip->i2c_capture_volume[n][1]= 0xcf;
+               /* snd_printk("I2C:array size=0x%x\n", size); */
+               for (n = 0; n < size; n++)
+                       snd_ca0106_i2c_write(chip, i2c_adc_init[n][0],
+                                            i2c_adc_init[n][1]);
+               for (n = 0; n < 4; n++) {
+                       chip->i2c_capture_volume[n][0] = 0xcf;
+                       chip->i2c_capture_volume[n][1] = 0xcf;
                }
-               chip->i2c_capture_source=2; /* Line in */
-               //snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */
+               chip->i2c_capture_source = 2; /* Line in */
+               /* Enable Line-in capture. MIC in currently untested. */
+               /* snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); */
        }
-        if (chip->details->spi_dac == 1) { /* The SB0570 use SPI to control DAC. */
+
+       if (chip->details->spi_dac == 1) {
+               /* The SB0570 use SPI to control DAC. */
                int size, n;
 
                size = ARRAY_SIZE(spi_dac_init);
@@ -1530,9 +1496,112 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
                                chip->spi_dac_reg[reg] = spi_dac_init[n];
                }
        }
+}
 
-       if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
-                                 chip, &ops)) < 0) {
+static void ca0106_stop_chip(struct snd_ca0106 *chip)
+{
+       /* disable interrupts */
+       snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
+       outl(0, chip->port + INTE);
+       snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
+       udelay(1000);
+       /* disable audio */
+       /* outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); */
+       outl(0, chip->port + HCFG);
+       /* FIXME: We need to stop and DMA transfers here.
+        *        But as I am not sure how yet, we cannot from the dma pages.
+        * So we can fix: snd-malloc: Memory leak?  pages not freed = 8
+        */
+}
+
+static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
+                                        struct pci_dev *pci,
+                                        struct snd_ca0106 **rchip)
+{
+       struct snd_ca0106 *chip;
+       struct snd_ca0106_details *c;
+       int err;
+       static struct snd_device_ops ops = {
+               .dev_free = snd_ca0106_dev_free,
+       };
+
+       *rchip = NULL;
+
+       err = pci_enable_device(pci);
+       if (err < 0)
+               return err;
+       if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 ||
+           pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) {
+               printk(KERN_ERR "error to set 32bit mask DMA\n");
+               pci_disable_device(pci);
+               return -ENXIO;
+       }
+
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       if (chip == NULL) {
+               pci_disable_device(pci);
+               return -ENOMEM;
+       }
+
+       chip->card = card;
+       chip->pci = pci;
+       chip->irq = -1;
+
+       spin_lock_init(&chip->emu_lock);
+
+       chip->port = pci_resource_start(pci, 0);
+       chip->res_port = request_region(chip->port, 0x20, "snd_ca0106");
+       if (!chip->res_port) {
+               snd_ca0106_free(chip);
+               printk(KERN_ERR "cannot allocate the port\n");
+               return -EBUSY;
+       }
+
+       if (request_irq(pci->irq, snd_ca0106_interrupt,
+                       IRQF_SHARED, "snd_ca0106", chip)) {
+               snd_ca0106_free(chip);
+               printk(KERN_ERR "cannot grab irq\n");
+               return -EBUSY;
+       }
+       chip->irq = pci->irq;
+
+       /* This stores the periods table. */
+       if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+                               1024, &chip->buffer) < 0) {
+               snd_ca0106_free(chip);
+               return -ENOMEM;
+       }
+
+       pci_set_master(pci);
+       /* read serial */
+       pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
+       pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
+       printk(KERN_INFO "snd-ca0106: Model %04x Rev %08x Serial %08x\n",
+              chip->model, pci->revision, chip->serial);
+       strcpy(card->driver, "CA0106");
+       strcpy(card->shortname, "CA0106");
+
+       for (c = ca0106_chip_details; c->serial; c++) {
+               if (subsystem[dev]) {
+                       if (c->serial == subsystem[dev])
+                               break;
+               } else if (c->serial == chip->serial)
+                       break;
+       }
+       chip->details = c;
+       if (subsystem[dev]) {
+               printk(KERN_INFO "snd-ca0106: Sound card name=%s, "
+                      "subsystem=0x%x. Forced to subsystem=0x%x\n",
+                      c->name, chip->serial, subsystem[dev]);
+       }
+
+       sprintf(card->longname, "%s at 0x%lx irq %i",
+               c->name, chip->port, chip->irq);
+
+       ca0106_init_chip(chip, 0);
+
+       err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+       if (err < 0) {
                snd_ca0106_free(chip);
                return err;
        }
@@ -1629,7 +1698,7 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
        static int dev;
        struct snd_card *card;
        struct snd_ca0106 *chip;
-       int err;
+       int i, err;
 
        if (dev >= SNDRV_CARDS)
                return -ENODEV;
@@ -1642,44 +1711,31 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
        if (card == NULL)
                return -ENOMEM;
 
-       if ((err = snd_ca0106_create(dev, card, pci, &chip)) < 0) {
-               snd_card_free(card);
-               return err;
-       }
+       err = snd_ca0106_create(dev, card, pci, &chip);
+       if (err < 0)
+               goto error;
+       card->private_data = chip;
 
-       if ((err = snd_ca0106_pcm(chip, 0, NULL)) < 0) {
-               snd_card_free(card);
-               return err;
-       }
-       if ((err = snd_ca0106_pcm(chip, 1, NULL)) < 0) {
-               snd_card_free(card);
-               return err;
-       }
-       if ((err = snd_ca0106_pcm(chip, 2, NULL)) < 0) {
-               snd_card_free(card);
-               return err;
-       }
-       if ((err = snd_ca0106_pcm(chip, 3, NULL)) < 0) {
-               snd_card_free(card);
-               return err;
-       }
-        if (chip->details->ac97 == 1) { /* The SB0410 and SB0413 do not have an AC97 chip. */
-               if ((err = snd_ca0106_ac97(chip)) < 0) {
-                       snd_card_free(card);
-                       return err;
-               }
+       for (i = 0; i < 4; i++) {
+               err = snd_ca0106_pcm(chip, i);
+               if (err < 0)
+                       goto error;
        }
-       if ((err = snd_ca0106_mixer(chip)) < 0) {
-               snd_card_free(card);
-               return err;
+
+       if (chip->details->ac97 == 1) {
+               /* The SB0410 and SB0413 do not have an AC97 chip. */
+               err = snd_ca0106_ac97(chip);
+               if (err < 0)
+                       goto error;
        }
+       err = snd_ca0106_mixer(chip);
+       if (err < 0)
+               goto error;
 
        snd_printdd("ca0106: probe for MIDI channel A ...");
-       if ((err = snd_ca0106_midi(chip,CA0106_MIDI_CHAN_A)) < 0) {
-               snd_card_free(card);
-               snd_printdd(" failed, err=0x%x\n",err);
-               return err;
-       }
+       err = snd_ca0106_midi(chip, CA0106_MIDI_CHAN_A);
+       if (err < 0)
+               goto error;
        snd_printdd(" done.\n");
 
 #ifdef CONFIG_PROC_FS
@@ -1688,14 +1744,17 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
 
        snd_card_set_dev(card, &pci->dev);
 
-       if ((err = snd_card_register(card)) < 0) {
-               snd_card_free(card);
-               return err;
-       }
+       err = snd_card_register(card);
+       if (err < 0)
+               goto error;
 
        pci_set_drvdata(pci, card);
        dev++;
        return 0;
+
+ error:
+       snd_card_free(card);
+       return err;
 }
 
 static void __devexit snd_ca0106_remove(struct pci_dev *pci)
@@ -1704,6 +1763,59 @@ static void __devexit snd_ca0106_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
+#ifdef CONFIG_PM
+static int snd_ca0106_suspend(struct pci_dev *pci, pm_message_t state)
+{
+       struct snd_card *card = pci_get_drvdata(pci);
+       struct snd_ca0106 *chip = card->private_data;
+       int i;
+
+       snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+       for (i = 0; i < 4; i++)
+               snd_pcm_suspend_all(chip->pcm[i]);
+       if (chip->details->ac97)
+               snd_ac97_suspend(chip->ac97);
+       snd_ca0106_mixer_suspend(chip);
+
+       ca0106_stop_chip(chip);
+
+       pci_disable_device(pci);
+       pci_save_state(pci);
+       pci_set_power_state(pci, pci_choose_state(pci, state));
+       return 0;
+}
+
+static int snd_ca0106_resume(struct pci_dev *pci)
+{
+       struct snd_card *card = pci_get_drvdata(pci);
+       struct snd_ca0106 *chip = card->private_data;
+       int i;
+
+       pci_set_power_state(pci, PCI_D0);
+       pci_restore_state(pci);
+
+       if (pci_enable_device(pci) < 0) {
+               snd_card_disconnect(card);
+               return -EIO;
+       }
+
+       pci_set_master(pci);
+
+       ca0106_init_chip(chip, 1);
+
+       if (chip->details->ac97)
+               snd_ac97_resume(chip->ac97);
+       snd_ca0106_mixer_resume(chip);
+       if (chip->details->spi_dac) {
+               for (i = 0; i < ARRAY_SIZE(chip->spi_dac_reg); i++)
+                       snd_ca0106_spi_write(chip, chip->spi_dac_reg[i]);
+       }
+
+       snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+       return 0;
+}
+#endif
+
 // PCI IDs
 static struct pci_device_id snd_ca0106_ids[] = {
        { 0x1102, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },    /* Audigy LS or Live 24bit */
@@ -1717,6 +1829,10 @@ static struct pci_driver driver = {
        .id_table = snd_ca0106_ids,
        .probe = snd_ca0106_probe,
        .remove = __devexit_p(snd_ca0106_remove),
+#ifdef CONFIG_PM
+       .suspend = snd_ca0106_suspend,
+       .resume = snd_ca0106_resume,
+#endif
 };
 
 // initialization of the module
index 3025ed1..ad28887 100644 (file)
 
 #include "ca0106.h"
 
+static void ca0106_spdif_enable(struct snd_ca0106 *emu)
+{
+       unsigned int val;
+
+       if (emu->spdif_enable) {
+               /* Digital */
+               snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
+               snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
+               val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000;
+               snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
+               val = inl(emu->port + GPIO) & ~0x101;
+               outl(val, emu->port + GPIO);
+
+       } else {
+               /* Analog */
+               snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
+               snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
+               val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000;
+               snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
+               val = inl(emu->port + GPIO) | 0x101;
+               outl(val, emu->port + GPIO);
+       }
+}
+
+static void ca0106_set_capture_source(struct snd_ca0106 *emu)
+{
+       unsigned int val = emu->capture_source;
+       unsigned int source, mask;
+       source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
+       mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
+       snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
+}
+
+static void ca0106_set_i2c_capture_source(struct snd_ca0106 *emu,
+                                         unsigned int val, int force)
+{
+       unsigned int ngain, ogain;
+       u32 source;
+
+       snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
+       ngain = emu->i2c_capture_volume[val][0]; /* Left */
+       ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
+       if (force || ngain != ogain)
+               snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ngain & 0xff);
+       ngain = emu->i2c_capture_volume[val][1]; /* Right */
+       ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */
+       if (force || ngain != ogain)
+               snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ngain & 0xff);
+       source = 1 << val;
+       snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */
+       emu->i2c_capture_source = val;
+}
+
+static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu)
+{
+       u32 tmp;
+
+       if (emu->capture_mic_line_in) {
+               /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
+               tmp = inl(emu->port+GPIO) & ~0x400;
+               tmp = tmp | 0x400;
+               outl(tmp, emu->port+GPIO);
+               /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */
+       } else {
+               /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
+               tmp = inl(emu->port+GPIO) & ~0x400;
+               outl(tmp, emu->port+GPIO);
+               /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */
+       }
+}
+
+static void ca0106_set_spdif_bits(struct snd_ca0106 *emu, int idx)
+{
+       snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, emu->spdif_str_bits[idx]);
+}
+
+/*
+ */
 static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
 static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
 
@@ -95,30 +173,12 @@ static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
        struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
        unsigned int val;
        int change = 0;
-       u32 mask;
 
        val = !!ucontrol->value.integer.value[0];
        change = (emu->spdif_enable != val);
        if (change) {
                emu->spdif_enable = val;
-               if (val) {
-                       /* Digital */
-                       snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
-                       snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
-                       snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
-                               snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000);
-                       mask = inl(emu->port + GPIO) & ~0x101;
-                       outl(mask, emu->port + GPIO);
-
-               } else {
-                       /* Analog */
-                       snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
-                       snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
-                       snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
-                               snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000);
-                       mask = inl(emu->port + GPIO) | 0x101;
-                       outl(mask, emu->port + GPIO);
-               }
+               ca0106_spdif_enable(emu);
        }
         return change;
 }
@@ -154,8 +214,6 @@ static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
        struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
        unsigned int val;
        int change = 0;
-       u32 mask;
-       u32 source;
 
        val = ucontrol->value.enumerated.item[0] ;
        if (val >= 6)
@@ -163,9 +221,7 @@ static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
        change = (emu->capture_source != val);
        if (change) {
                emu->capture_source = val;
-               source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
-               mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
-               snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
+               ca0106_set_capture_source(emu);
        }
         return change;
 }
@@ -200,9 +256,7 @@ static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
 {
        struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
        unsigned int source_id;
-       unsigned int ngain, ogain;
        int change = 0;
-       u32 source;
        /* If the capture source has changed,
         * update the capture volume from the cached value
         * for the particular source.
@@ -212,18 +266,7 @@ static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
                return -EINVAL;
        change = (emu->i2c_capture_source != source_id);
        if (change) {
-               snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
-               ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
-               ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
-               if (ngain != ogain)
-                       snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff));
-               ngain = emu->i2c_capture_volume[source_id][1]; /* Left */
-               ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Left */
-               if (ngain != ogain)
-                       snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
-               source = 1 << source_id;
-               snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */
-               emu->i2c_capture_source = source_id;
+               ca0106_set_i2c_capture_source(emu, source_id, 0);
        }
         return change;
 }
@@ -271,7 +314,6 @@ static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
        struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
        unsigned int val;
        int change = 0;
-       u32 tmp;
 
        val = ucontrol->value.enumerated.item[0] ;
        if (val > 1)
@@ -279,18 +321,7 @@ static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
        change = (emu->capture_mic_line_in != val);
        if (change) {
                emu->capture_mic_line_in = val;
-               if (val) {
-                       //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
-                       tmp = inl(emu->port+GPIO) & ~0x400;
-                       tmp = tmp | 0x400;
-                       outl(tmp, emu->port+GPIO);
-                       //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC);
-               } else {
-                       //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
-                       tmp = inl(emu->port+GPIO) & ~0x400;
-                       outl(tmp, emu->port+GPIO);
-                       //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN);
-               }
+               ca0106_set_capture_mic_line_in(emu);
        }
         return change;
 }
@@ -322,16 +353,33 @@ static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
-static int snd_ca0106_spdif_get(struct snd_kcontrol *kcontrol,
+static void decode_spdif_bits(unsigned char *status, unsigned int bits)
+{
+       status[0] = (bits >> 0) & 0xff;
+       status[1] = (bits >> 8) & 0xff;
+       status[2] = (bits >> 16) & 0xff;
+       status[3] = (bits >> 24) & 0xff;
+}
+
+static int snd_ca0106_spdif_get_default(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
        unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 
-       ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
-       ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
-       ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
-       ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
+       decode_spdif_bits(ucontrol->value.iec958.status,
+                         emu->spdif_bits[idx]);
+        return 0;
+}
+
+static int snd_ca0106_spdif_get_stream(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+       decode_spdif_bits(ucontrol->value.iec958.status,
+                         emu->spdif_str_bits[idx]);
         return 0;
 }
 
@@ -345,24 +393,48 @@ static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
         return 0;
 }
 
-static int snd_ca0106_spdif_put(struct snd_kcontrol *kcontrol,
+static unsigned int encode_spdif_bits(unsigned char *status)
+{
+       return ((unsigned int)status[0] << 0) |
+               ((unsigned int)status[1] << 8) |
+               ((unsigned int)status[2] << 16) |
+               ((unsigned int)status[3] << 24);
+}
+
+static int snd_ca0106_spdif_put_default(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
        unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-       int change;
        unsigned int val;
 
-       val = (ucontrol->value.iec958.status[0] << 0) |
-             (ucontrol->value.iec958.status[1] << 8) |
-             (ucontrol->value.iec958.status[2] << 16) |
-             (ucontrol->value.iec958.status[3] << 24);
-       change = val != emu->spdif_bits[idx];
-       if (change) {
-               snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val);
+       val = encode_spdif_bits(ucontrol->value.iec958.status);
+       if (val != emu->spdif_bits[idx]) {
                emu->spdif_bits[idx] = val;
+               /* FIXME: this isn't safe, but needed to keep the compatibility
+                * with older alsa-lib config
+                */
+               emu->spdif_str_bits[idx] = val;
+               ca0106_set_spdif_bits(emu, idx);
+               return 1;
        }
-        return change;
+       return 0;
+}
+
+static int snd_ca0106_spdif_put_stream(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+       unsigned int val;
+
+       val = encode_spdif_bits(ucontrol->value.iec958.status);
+       if (val != emu->spdif_str_bits[idx]) {
+               emu->spdif_str_bits[idx] = val;
+               ca0106_set_spdif_bits(emu, idx);
+               return 1;
+       }
+        return 0;
 }
 
 static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
@@ -573,8 +645,16 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
                .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
                .count =        4,
                .info =         snd_ca0106_spdif_info,
-               .get =          snd_ca0106_spdif_get,
-               .put =          snd_ca0106_spdif_put
+               .get =          snd_ca0106_spdif_get_default,
+               .put =          snd_ca0106_spdif_put_default
+       },
+       {
+               .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+               .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+               .count =        4,
+               .info =         snd_ca0106_spdif_info,
+               .get =          snd_ca0106_spdif_get_stream,
+               .put =          snd_ca0106_spdif_put_stream
        },
 };
 
@@ -773,3 +853,50 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
         return 0;
 }
 
+#ifdef CONFIG_PM
+struct ca0106_vol_tbl {
+       unsigned int channel_id;
+       unsigned int reg;
+};
+
+static struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = {
+       { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 },
+       { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 },
+       { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 },
+       { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2 },
+       { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1 },
+       { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1 },
+       { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1 },
+       { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1 },
+       { 1, CAPTURE_CONTROL },
+};
+
+void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip)
+{
+       int i;
+
+       /* save volumes */
+       for (i = 0; i < NUM_SAVED_VOLUMES; i++)
+               chip->saved_vol[i] =
+                       snd_ca0106_ptr_read(chip, saved_volumes[i].reg,
+                                           saved_volumes[i].channel_id);
+}
+
+void snd_ca0106_mixer_resume(struct snd_ca0106  *chip)
+{
+       int i;
+
+       for (i = 0; i < NUM_SAVED_VOLUMES; i++)
+               snd_ca0106_ptr_write(chip, saved_volumes[i].reg,
+                                    saved_volumes[i].channel_id,
+                                    chip->saved_vol[i]);
+
+       ca0106_spdif_enable(chip);
+       ca0106_set_capture_source(chip);
+       ca0106_set_i2c_capture_source(chip, chip->i2c_capture_source, 1);
+       for (i = 0; i < 4; i++)
+               ca0106_set_spdif_bits(chip, i);
+       if (chip->details->i2c_adc)
+               ca0106_set_capture_mic_line_in(chip);
+}
+#endif /* CONFIG_PM */
index fb6dc39..8ab07aa 100644 (file)
@@ -3640,7 +3640,10 @@ int snd_cs46xx_resume(struct pci_dev *pci)
 {
        struct snd_card *card = pci_get_drvdata(pci);
        struct snd_cs46xx *chip = card->private_data;
-       int i, amp_saved;
+       int amp_saved;
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+       int i;
+#endif
 
        pci_set_power_state(pci, PCI_D0);
        pci_restore_state(pci);
index bb3d57e..fda7a94 100644 (file)
@@ -4,6 +4,9 @@
 
 snd-cs5535audio-y := cs5535audio.o cs5535audio_pcm.o
 snd-cs5535audio-$(CONFIG_PM) += cs5535audio_pm.o
+ifdef CONFIG_MGEODE_LX
+snd-cs5535audio-$(CONFIG_OLPC) += cs5535audio_olpc.o
+endif
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o
index 1d8b160..826e6de 100644 (file)
@@ -159,10 +159,14 @@ static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
                return err;
 
        memset(&ac97, 0, sizeof(ac97));
-       ac97.scaps = AC97_SCAP_AUDIO|AC97_SCAP_SKIP_MODEM;
+       ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM
+                       | AC97_SCAP_POWER_SAVE;
        ac97.private_data = cs5535au;
        ac97.pci = cs5535au->pci;
 
+       /* set any OLPC-specific scaps */
+       olpc_prequirks(card, &ac97);
+
        if ((err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97)) < 0) {
                snd_printk(KERN_ERR "mixer failed\n");
                return err;
@@ -170,6 +174,12 @@ static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
 
        snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk);
 
+       err = olpc_quirks(card, cs5535au->ac97);
+       if (err < 0) {
+               snd_printk(KERN_ERR "olpc quirks failed\n");
+               return err;
+       }
+
        return 0;
 }
 
index 66bae76..7a298ac 100644 (file)
@@ -78,6 +78,7 @@ struct cs5535audio_dma {
        unsigned int buf_addr, buf_bytes;
        unsigned int period_bytes, periods;
        u32 saved_prd;
+       int pcm_open_flag;
 };
 
 struct cs5535audio {
@@ -93,8 +94,46 @@ struct cs5535audio {
        struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS];
 };
 
+#ifdef CONFIG_PM
 int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state);
 int snd_cs5535audio_resume(struct pci_dev *pci);
+#endif
+
+#if defined(CONFIG_OLPC) && defined(CONFIG_MGEODE_LX)
+void __devinit olpc_prequirks(struct snd_card *card,
+               struct snd_ac97_template *ac97);
+int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97);
+void olpc_analog_input(struct snd_ac97 *ac97, int on);
+void olpc_mic_bias(struct snd_ac97 *ac97, int on);
+
+static inline void olpc_capture_open(struct snd_ac97 *ac97)
+{
+       /* default to Analog Input off */
+       olpc_analog_input(ac97, 0);
+       /* enable MIC Bias for recording */
+       olpc_mic_bias(ac97, 1);
+}
+
+static inline void olpc_capture_close(struct snd_ac97 *ac97)
+{
+       /* disable Analog Input */
+       olpc_analog_input(ac97, 0);
+       /* disable the MIC Bias (so the recording LED turns off) */
+       olpc_mic_bias(ac97, 0);
+}
+#else
+static inline void olpc_prequirks(struct snd_card *card,
+               struct snd_ac97_template *ac97) { }
+static inline int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
+{
+       return 0;
+}
+static inline void olpc_analog_input(struct snd_ac97 *ac97, int on) { }
+static inline void olpc_mic_bias(struct snd_ac97 *ac97, int on) { }
+static inline void olpc_capture_open(struct snd_ac97 *ac97) { }
+static inline void olpc_capture_close(struct snd_ac97 *ac97) { }
+#endif
+
 int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535audio);
 
 #endif /* __SOUND_CS5535AUDIO_H */
diff --git a/sound/pci/cs5535audio/cs5535audio_olpc.c b/sound/pci/cs5535audio/cs5535audio_olpc.c
new file mode 100644 (file)
index 0000000..5c68143
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * OLPC XO-1 additional sound features
+ *
+ * Copyright Â© 2006  Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright Â© 2007-2008  Andres Salomon <dilinger@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/ac97_codec.h>
+
+#include <asm/olpc.h>
+#include "cs5535audio.h"
+
+/*
+ * OLPC has an additional feature on top of the regular AD1888 codec features.
+ * It has an Analog Input mode that is switched into (after disabling the
+ * High Pass Filter) via GPIO.  It is supported on B2 and later models.
+ */
+void olpc_analog_input(struct snd_ac97 *ac97, int on)
+{
+       int err;
+
+       if (!machine_is_olpc())
+               return;
+
+       /* update the High Pass Filter (via AC97_AD_TEST2) */
+       err = snd_ac97_update_bits(ac97, AC97_AD_TEST2,
+                       1 << AC97_AD_HPFD_SHIFT, on << AC97_AD_HPFD_SHIFT);
+       if (err < 0) {
+               snd_printk(KERN_ERR "setting High Pass Filter - %d\n", err);
+               return;
+       }
+
+       /* set Analog Input through GPIO */
+       if (on)
+               geode_gpio_set(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL);
+       else
+               geode_gpio_clear(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL);
+}
+
+/*
+ * OLPC XO-1's V_REFOUT is a mic bias enable.
+ */
+void olpc_mic_bias(struct snd_ac97 *ac97, int on)
+{
+       int err;
+
+       if (!machine_is_olpc())
+               return;
+
+       on = on ? 0 : 1;
+       err = snd_ac97_update_bits(ac97, AC97_AD_MISC,
+                       1 << AC97_AD_VREFD_SHIFT, on << AC97_AD_VREFD_SHIFT);
+       if (err < 0)
+               snd_printk(KERN_ERR "setting MIC Bias - %d\n", err);
+}
+
+static int olpc_dc_info(struct snd_kcontrol *kctl,
+               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int olpc_dc_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
+{
+       v->value.integer.value[0] = geode_gpio_isset(OLPC_GPIO_MIC_AC,
+                       GPIO_OUTPUT_VAL);
+       return 0;
+}
+
+static int olpc_dc_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
+{
+       struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
+
+       olpc_analog_input(cs5535au->ac97, v->value.integer.value[0]);
+       return 1;
+}
+
+static int olpc_mic_info(struct snd_kcontrol *kctl,
+               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int olpc_mic_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
+{
+       struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
+       struct snd_ac97 *ac97 = cs5535au->ac97;
+       int i;
+
+       i = (snd_ac97_read(ac97, AC97_AD_MISC) >> AC97_AD_VREFD_SHIFT) & 0x1;
+       v->value.integer.value[0] = i ? 0 : 1;
+       return 0;
+}
+
+static int olpc_mic_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
+{
+       struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
+
+       olpc_mic_bias(cs5535au->ac97, v->value.integer.value[0]);
+       return 1;
+}
+
+static struct snd_kcontrol_new olpc_cs5535audio_ctls[] __devinitdata = {
+{
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "DC Mode Enable",
+       .info = olpc_dc_info,
+       .get = olpc_dc_get,
+       .put = olpc_dc_put,
+       .private_value = 0,
+},
+{
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "MIC Bias Enable",
+       .info = olpc_mic_info,
+       .get = olpc_mic_get,
+       .put = olpc_mic_put,
+       .private_value = 0,
+},
+};
+
+void __devinit olpc_prequirks(struct snd_card *card,
+               struct snd_ac97_template *ac97)
+{
+       if (!machine_is_olpc())
+               return;
+
+       /* invert EAPD if on an OLPC B3 or higher */
+       if (olpc_board_at_least(olpc_board_pre(0xb3)))
+               ac97->scaps |= AC97_SCAP_INV_EAPD;
+}
+
+int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
+{
+       struct snd_ctl_elem_id elem;
+       int i, err;
+
+       if (!machine_is_olpc())
+               return 0;
+
+       /* drop the original AD1888 HPF control */
+       memset(&elem, 0, sizeof(elem));
+       elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       strncpy(elem.name, "High Pass Filter Enable", sizeof(elem.name));
+       snd_ctl_remove_id(card, &elem);
+
+       /* drop the original V_REFOUT control */
+       memset(&elem, 0, sizeof(elem));
+       elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       strncpy(elem.name, "V_REFOUT Enable", sizeof(elem.name));
+       snd_ctl_remove_id(card, &elem);
+
+       /* add the OLPC-specific controls */
+       for (i = 0; i < ARRAY_SIZE(olpc_cs5535audio_ctls); i++) {
+               err = snd_ctl_add(card, snd_ctl_new1(&olpc_cs5535audio_ctls[i],
+                               ac97->private_data));
+               if (err < 0)
+                       return err;
+       }
+
+       /* turn off the mic by default */
+       olpc_mic_bias(ac97, 0);
+       return 0;
+}
index cdcda87..0f48a87 100644 (file)
@@ -260,6 +260,9 @@ static int snd_cs5535audio_hw_params(struct snd_pcm_substream *substream,
        err = cs5535audio_build_dma_packets(cs5535au, dma, substream,
                                            params_periods(hw_params),
                                            params_period_bytes(hw_params));
+       if (!err)
+               dma->pcm_open_flag = 1;
+
        return err;
 }
 
@@ -268,6 +271,15 @@ static int snd_cs5535audio_hw_free(struct snd_pcm_substream *substream)
        struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
        struct cs5535audio_dma *dma = substream->runtime->private_data;
 
+       if (dma->pcm_open_flag) {
+               if (substream == cs5535au->playback_substream)
+                       snd_ac97_update_power(cs5535au->ac97,
+                                       AC97_PCM_FRONT_DAC_RATE, 0);
+               else
+                       snd_ac97_update_power(cs5535au->ac97,
+                                       AC97_PCM_LR_ADC_RATE, 0);
+               dma->pcm_open_flag = 0;
+       }
        cs5535audio_clear_dma_packets(cs5535au, dma, substream);
        return snd_pcm_lib_free_pages(substream);
 }
@@ -351,11 +363,14 @@ static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream)
        if ((err = snd_pcm_hw_constraint_integer(runtime,
                                         SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
                return err;
+       olpc_capture_open(cs5535au->ac97);
        return 0;
 }
 
 static int snd_cs5535audio_capture_close(struct snd_pcm_substream *substream)
 {
+       struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
+       olpc_capture_close(cs5535au->ac97);
        return 0;
 }
 
index de5ee8f..7958006 100644 (file)
@@ -69,7 +69,7 @@ MODULE_FIRMWARE(EMU1010_NOTEBOOK_FILENAME);
  * EMU10K1 init / done
  *************************************************************************/
 
-void snd_emu10k1_voice_init(struct snd_emu10k1 * emu, int ch)
+void snd_emu10k1_voice_init(struct snd_emu10k1 *emu, int ch)
 {
        snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0);
        snd_emu10k1_ptr_write(emu, IP, ch, 0);
@@ -151,9 +151,9 @@ static unsigned int i2c_adc_init[][2] = {
        { 0x12, 0x32 },  /* ALC Control 3 */
        { 0x13, 0x00 },  /* Noise gate control */
        { 0x14, 0xa6 },  /* Limiter control */
-       { 0x15, ADC_MUX_2 },  /* ADC Mixer control. Mic for Audigy 2 ZS Notebook */
+       { 0x15, ADC_MUX_2 },  /* ADC Mixer control. Mic for A2ZS Notebook */
 };
-       
+
 static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
 {
        unsigned int silent_page;
@@ -161,8 +161,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
        u32 tmp;
 
        /* disable audio and lock cache */
-       outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE,
-            emu->port + HCFG);
+       outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK |
+               HCFG_MUTEBUTTONENABLE, emu->port + HCFG);
 
        /* reset recording buffers */
        snd_emu10k1_ptr_write(emu, MICBS, 0, ADCBS_BUFSIZE_NONE);
@@ -179,7 +179,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
        snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
        snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
 
-       if (emu->audigy){
+       if (emu->audigy) {
                /* set SPDIF bypass mode */
                snd_emu10k1_ptr_write(emu, SPBYPASS, 0, SPBYPASS_FORMAT);
                /* enable rear left + rear right AC97 slots */
@@ -197,12 +197,12 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
 
        if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
                /* Hacks for Alice3 to work independent of haP16V driver */
-               //Setup SRCMulti_I2S SamplingRate
+               /* 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) */
                snd_emu10k1_ptr20_write(emu, SRCSel, 0, 0x14);
                /* Setup SRCMulti Input Audio Enable */
@@ -217,7 +217,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
        if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */
                /* Hacks for Alice3 to work independent of haP16V driver */
                snd_printk(KERN_INFO "Audigy2 value: Special config.\n");
-               //Setup SRCMulti_I2S SamplingRate
+               /* Setup SRCMulti_I2S SamplingRate */
                tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
                tmp &= 0xfffff1ff;
                tmp |= (0x2<<9);
@@ -270,13 +270,13 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
                size = ARRAY_SIZE(i2c_adc_init);
                for (n = 0; n < size; n++)
                        snd_emu10k1_i2c_write(emu, i2c_adc_init[n][0], i2c_adc_init[n][1]);
-               for (n=0; n < 4; n++) {
-                       emu->i2c_capture_volume[n][0]= 0xcf;
-                       emu->i2c_capture_volume[n][1]= 0xcf;
+               for (n = 0; n < 4; n++) {
+                       emu->i2c_capture_volume[n][0] = 0xcf;
+                       emu->i2c_capture_volume[n][1] = 0xcf;
                }
        }
 
-       
+
        snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages.addr);
        snd_emu10k1_ptr_write(emu, TCB, 0, 0);  /* taken from original driver */
        snd_emu10k1_ptr_write(emu, TCBS, 0, 4); /* taken from original driver */
@@ -313,7 +313,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
            (emu->model == 0x21 && emu->revision < 6))
                outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE, emu->port + HCFG);
        else
-               // With on-chip joystick
+               /* With on-chip joystick */
                outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
 
        if (enable_ir) {        /* enable IR for SB Live */
@@ -335,9 +335,9 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
                        outl(reg | HCFG_GPOUT1 | HCFG_GPOUT2, emu->port + HCFG);
                        udelay(100);
                        outl(reg, emu->port + HCFG);
-               }
+               }
        }
-       
+
        if (emu->card_capabilities->emu_model) {
                ;  /* Disable all access to A_IOCFG for the emu1010 */
        } else if (emu->card_capabilities->i2c_adc) {
@@ -364,7 +364,7 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
                ;  /* Disable A_IOCFG for Audigy 2 ZS Notebook */
        } else if (emu->audigy) {
                outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
+
                if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
                        /* Unmute Analog now.  Set GPO6 to 1 for Apollo.
                         * This has to be done after init ALice3 I2SOut beyond 48KHz.
@@ -378,12 +378,12 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
                        outl(inl(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG);
                }
        }
-       
+
 #if 0
        {
        unsigned int tmp;
        /* FIXME: the following routine disables LiveDrive-II !! */
-       // TOSLink detection
+       /* TOSLink detection */
        emu->tos_link = 0;
        tmp = inl(emu->port + HCFG);
        if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) {
@@ -400,7 +400,7 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
        snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE);
 }
 
-int snd_emu10k1_done(struct snd_emu10k1 * emu)
+int snd_emu10k1_done(struct snd_emu10k1 *emu)
 {
        int ch;
 
@@ -495,7 +495,7 @@ int snd_emu10k1_done(struct snd_emu10k1 * emu)
 
 #define EC_LAST_PROMFILE_ADDR  0x2f
 
-#define EC_SERIALNUM_ADDR      0x30    /* First word of serial number.  The 
+#define EC_SERIALNUM_ADDR      0x30    /* First word of serial number.  The
                                         * can be up to 30 characters in length
                                         * and is stored as a NULL-terminated
                                         * ASCII string.  Any unused bytes must be
@@ -503,8 +503,8 @@ int snd_emu10k1_done(struct snd_emu10k1 * emu)
 #define EC_CHECKSUM_ADDR       0x3f    /* Location at which checksum is stored */
 
 
-/* Most of this stuff is pretty self-evident.  According to the hardware 
- * dudes, we need to leave the ADCCAL bit low in order to avoid a DC 
+/* Most of this stuff is pretty self-evident.  According to the hardware
+ * dudes, we need to leave the ADCCAL bit low in order to avoid a DC
  * offset problem.  Weird.
  */
 #define EC_RAW_RUN_MODE                (EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | \
@@ -523,7 +523,7 @@ int snd_emu10k1_done(struct snd_emu10k1 * emu)
  *  register.
  */
 
-static void snd_emu10k1_ecard_write(struct snd_emu10k1 * emu, unsigned int value)
+static void snd_emu10k1_ecard_write(struct snd_emu10k1 *emu, unsigned int value)
 {
        unsigned short count;
        unsigned int data;
@@ -561,7 +561,7 @@ static void snd_emu10k1_ecard_write(struct snd_emu10k1 * emu, unsigned int value
  * channel.
  */
 
-static void snd_emu10k1_ecard_setadcgain(struct snd_emu10k1 * emu,
+static void snd_emu10k1_ecard_setadcgain(struct snd_emu10k1 *emu,
                                         unsigned short gain)
 {
        unsigned int bit;
@@ -574,7 +574,7 @@ static void snd_emu10k1_ecard_setadcgain(struct snd_emu10k1 * emu,
 
        for (bit = (1 << 15); bit; bit >>= 1) {
                unsigned int value;
-               
+
                value = emu->ecard_ctrl & ~(EC_TRIM_CSN | EC_TRIM_SDATA);
 
                if (gain & bit)
@@ -589,7 +589,7 @@ static void snd_emu10k1_ecard_setadcgain(struct snd_emu10k1 * emu,
        snd_emu10k1_ecard_write(emu, emu->ecard_ctrl);
 }
 
-static int snd_emu10k1_ecard_init(struct snd_emu10k1 * emu)
+static int snd_emu10k1_ecard_init(struct snd_emu10k1 *emu)
 {
        unsigned int hc_value;
 
@@ -598,7 +598,7 @@ static int snd_emu10k1_ecard_init(struct snd_emu10k1 * emu)
                          EC_SPDIF0_SELECT(EC_DEFAULT_SPDIF0_SEL) |
                          EC_SPDIF1_SELECT(EC_DEFAULT_SPDIF1_SEL);
 
-       /* Step 0: Set the codec type in the hardware control register 
+       /* Step 0: Set the codec type in the hardware control register
         * and enable audio output */
        hc_value = inl(emu->port + HCFG);
        outl(hc_value | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S, emu->port + HCFG);
@@ -629,7 +629,7 @@ static int snd_emu10k1_ecard_init(struct snd_emu10k1 * emu)
        return 0;
 }
 
-static int snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu)
+static int snd_emu10k1_cardbus_init(struct snd_emu10k1 *emu)
 {
        unsigned long special_port;
        unsigned int value;
@@ -656,7 +656,7 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu)
        return 0;
 }
 
-static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * filename)
+static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, const char *filename)
 {
        int err;
        int n, i;
@@ -666,11 +666,12 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
        unsigned long flags;
        const struct firmware *fw_entry;
 
-       if ((err = request_firmware(&fw_entry, filename, &emu->pci->dev)) != 0) {
-               snd_printk(KERN_ERR "firmware: %s not found. Err=%d\n",filename, err);
+       err = request_firmware(&fw_entry, filename, &emu->pci->dev);
+       if (err != 0) {
+               snd_printk(KERN_ERR "firmware: %s not found. Err = %d\n", filename, err);
                return err;
        }
-       snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size);
+       snd_printk(KERN_INFO "firmware size = 0x%zx\n", fw_entry->size);
 
        /* The FPGA is a Xilinx Spartan IIE XC2S50E */
        /* GPIO7 -> FPGA PGMN
@@ -685,13 +686,13 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
        outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */
        write_post = inl(emu->port + A_IOCFG);
        udelay(100); /* Allow FPGA memory to clean */
-       for(n = 0; n < fw_entry->size; n++) {
-               value=fw_entry->data[n];        
-               for(i = 0; i < 8; i++) {
+       for (n = 0; n < fw_entry->size; n++) {
+               value = fw_entry->data[n];
+               for (i = 0; i < 8; i++) {
                        reg = 0x80;
                        if (value & 0x1)
                                reg = reg | 0x20;
-                       value = value >> 1;   
+                       value = value >> 1;
                        outl(reg, emu->port + A_IOCFG);
                        write_post = inl(emu->port + A_IOCFG);
                        outl(reg | 0x40, emu->port + A_IOCFG);
@@ -703,14 +704,14 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
        write_post = inl(emu->port + A_IOCFG);
        spin_unlock_irqrestore(&emu->emu_lock, flags);
 
-        release_firmware(fw_entry);
+       release_firmware(fw_entry);
        return 0;
 }
 
 static int emu1010_firmware_thread(void *data)
 {
-       struct snd_emu10k1 * emu = data;
-       int tmp,tmp2;
+       struct snd_emu10k1 *emu = data;
+       int tmp, tmp2;
        int reg;
        int err;
 
@@ -719,50 +720,50 @@ static int emu1010_firmware_thread(void *data)
                msleep_interruptible(1000);
                if (kthread_should_stop())
                        break;
-               snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp ); /* IRQ Status */
-               snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg ); /* OPTIONS: Which cards are attached to the EMU */
+               snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp); /* IRQ Status */
+               snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg); /* OPTIONS: Which cards are attached to the EMU */
                if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) {
                        /* Audio Dock attached */
                        /* Return to Audio Dock programming mode */
                        snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n");
-                       snd_emu1010_fpga_write(emu,  EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK );
+                       snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK);
                        if (emu->card_capabilities->emu_model ==
                            EMU_MODEL_EMU1010) {
-                               if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) {
+                               err = snd_emu1010_load_firmware(emu, DOCK_FILENAME);
+                               if (err != 0)
                                        continue;
-                               }
                        } else if (emu->card_capabilities->emu_model ==
                                   EMU_MODEL_EMU1010B) {
-                               if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
+                               err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME);
+                               if (err != 0)
                                        continue;
-                               }
                        } else if (emu->card_capabilities->emu_model ==
                                   EMU_MODEL_EMU1616) {
-                               if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
+                               err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME);
+                               if (err != 0)
                                        continue;
-                               }
                        }
 
-                       snd_emu1010_fpga_write(emu,  EMU_HANA_FPGA_CONFIG, 0 );
-                       snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg );
-                       snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg);
+                       snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0);
+                       snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg);
+                       snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS = 0x%x\n", reg);
                        /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
-                       snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
-                       snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID=0x%x\n",reg);
+                       snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
+                       snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", reg);
                        if ((reg & 0x1f) != 0x15) {
                                /* FPGA failed to be programmed */
-                               snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg);
+                               snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n", reg);
                                continue;
                        }
                        snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n");
-                       snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp );
-                       snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2 );
-                       snd_printk("Audio Dock ver:%d.%d\n",tmp ,tmp2);
+                       snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp);
+                       snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2);
+                       snd_printk("Audio Dock ver:%d.%d\n", tmp, tmp2);
                        /* Sync clocking between 1010 and Dock */
                        /* Allow DLL to settle */
                        msleep(10);
                        /* Unmute all. Default is muted after a firmware load */
-                       snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
+                       snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
                }
        }
        snd_printk(KERN_INFO "emu1010: firmware thread stopping\n");
@@ -800,10 +801,10 @@ static int emu1010_firmware_thread(void *data)
  *             16 x 16-bit playback - snd_emu10k1_fx8010_playback_ops
  *             16 x 32-bit capture - snd_emu10k1_capture_efx_ops
  */
-static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
+static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
 {
        unsigned int i;
-       int tmp,tmp2;
+       int tmp, tmp2;
        int reg;
        int err;
        const char *filename = NULL;
@@ -818,7 +819,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
         * Lock Tank Memory Cache,
         * Mute all codecs.
         */
-       outl(0x0005a004, emu->port + HCFG); 
+       outl(0x0005a004, emu->port + HCFG);
        /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
         * Mute all codecs.
         */
@@ -829,25 +830,25 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
        outl(0x0005a000, emu->port + HCFG);
 
        /* Disable 48Volt power to Audio Dock */
-       snd_emu1010_fpga_write(emu,  EMU_HANA_DOCK_PWR,  0 );
+       snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
 
        /* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */
-       snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
-       snd_printdd("reg1=0x%x\n",reg);
+       snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
+       snd_printdd("reg1 = 0x%x\n", reg);
        if ((reg & 0x3f) == 0x15) {
                /* FPGA netlist already present so clear it */
                /* Return to programming mode */
 
-               snd_emu1010_fpga_write(emu,  EMU_HANA_FPGA_CONFIG, 0x02 );
+               snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0x02);
        }
-       snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
-       snd_printdd("reg2=0x%x\n",reg);
+       snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
+       snd_printdd("reg2 = 0x%x\n", reg);
        if ((reg & 0x3f) == 0x15) {
                /* FPGA failed to return to programming mode */
                snd_printk(KERN_INFO "emu1010: FPGA failed to return to programming mode\n");
                return -ENODEV;
        }
-       snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg);
+       snd_printk(KERN_INFO "emu1010: EMU_HANA_ID = 0x%x\n", reg);
        switch (emu->card_capabilities->emu_model) {
        case EMU_MODEL_EMU1010:
                filename = HANA_FILENAME;
@@ -876,25 +877,25 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
        }
 
        /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
-       snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
+       snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
        if ((reg & 0x3f) != 0x15) {
                /* FPGA failed to be programmed */
-               snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg=0x%x\n", reg);
+               snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg = 0x%x\n", reg);
                return -ENODEV;
        }
 
        snd_printk(KERN_INFO "emu1010: Hana Firmware loaded\n");
-       snd_emu1010_fpga_read(emu, EMU_HANA_MAJOR_REV, &tmp );
-       snd_emu1010_fpga_read(emu, EMU_HANA_MINOR_REV, &tmp2 );
-       snd_printk("Hana ver:%d.%d\n",tmp ,tmp2);
+       snd_emu1010_fpga_read(emu, EMU_HANA_MAJOR_REV, &tmp);
+       snd_emu1010_fpga_read(emu, EMU_HANA_MINOR_REV, &tmp2);
+       snd_printk("emu1010: Hana version: %d.%d\n", tmp, tmp2);
        /* Enable 48Volt power to Audio Dock */
-       snd_emu1010_fpga_write(emu,  EMU_HANA_DOCK_PWR,  EMU_HANA_DOCK_PWR_ON );
+       snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, EMU_HANA_DOCK_PWR_ON);
 
-       snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg );
-       snd_printk(KERN_INFO "emu1010: Card options=0x%x\n",reg);
-       snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg );
-       snd_printk(KERN_INFO "emu1010: Card options=0x%x\n",reg);
-       snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp ); 
+       snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
+       snd_printk(KERN_INFO "emu1010: Card options = 0x%x\n", reg);
+       snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
+       snd_printk(KERN_INFO "emu1010: Card options = 0x%x\n", reg);
+       snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp);
        /* Optical -> ADAT I/O  */
        /* 0 : SPDIF
         * 1 : ADAT
@@ -904,41 +905,42 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
        tmp = 0;
        tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) |
                (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0);
-       snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp );
-       snd_emu1010_fpga_read(emu, EMU_HANA_ADC_PADS, &tmp );
+       snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp);
+       snd_emu1010_fpga_read(emu, EMU_HANA_ADC_PADS, &tmp);
        /* Set no attenuation on Audio Dock pads. */
-       snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, 0x00 );
+       snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, 0x00);
        emu->emu1010.adc_pads = 0x00;
-       snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp );
+       snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp);
        /* Unmute Audio dock DACs, Headphone source DAC-4. */
-       snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30 );
-       snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12 );
-       snd_emu1010_fpga_read(emu, EMU_HANA_DAC_PADS, &tmp );
+       snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30);
+       snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);
+       snd_emu1010_fpga_read(emu, EMU_HANA_DAC_PADS, &tmp);
        /* DAC PADs. */
-       snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, 0x0f );
+       snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, 0x0f);
        emu->emu1010.dac_pads = 0x0f;
-       snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp );
-       snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30 );
-       snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp );
+       snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp);
+       snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30);
+       snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp);
        /* SPDIF Format. Set Consumer mode, 24bit, copy enable */
-       snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 );
+       snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10);
        /* MIDI routing */
-       snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 );
+       snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19);
        /* Unknown. */
-       snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c );
-       /* snd_emu1010_fpga_write(emu, 0x09, 0x0f ); // IRQ Enable: All on */
+       snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c);
+       /* IRQ Enable: Alll on */
+       /* snd_emu1010_fpga_write(emu, 0x09, 0x0f ); */
        /* IRQ Enable: All off */
-       snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00 );
+       snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00);
 
-       snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg );
-       snd_printk(KERN_INFO "emu1010: Card options3=0x%x\n",reg);
+       snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
+       snd_printk(KERN_INFO "emu1010: Card options3 = 0x%x\n", reg);
        /* Default WCLK set to 48kHz. */
-       snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x00 );
+       snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x00);
        /* Word Clock source, Internal 48kHz x1 */
-       snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K );
-       //snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X );
+       snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K);
+       /* snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X); */
        /* Audio Dock LEDs. */
-       snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12 );
+       snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);
 
 #if 0
        /* For 96kHz */
@@ -992,7 +994,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
         * Defaults only, users will set their own values anyways, let's
         * just copy/paste.
         */
-       
+
        snd_emu1010_fpga_link_dst_src_write(emu,
                EMU_DST_ALICE2_EMU32_8, EMU_SRC_DOCK_MIC_A1);
        snd_emu1010_fpga_link_dst_src_write(emu,
@@ -1037,19 +1039,19 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
        snd_emu1010_fpga_link_dst_src_write(emu,
                EMU_DST_ALICE2_EMU32_F, EMU_SRC_HAMOA_ADC_LEFT2);
 #endif
-       for (i = 0;i < 0x20; i++ ) {
-               /* AudioDock Elink <-  Silence */
-               snd_emu1010_fpga_link_dst_src_write(emu, 0x0100+i, EMU_SRC_SILENCE);
+       for (i = 0; i < 0x20; i++) {
+               /* AudioDock Elink <- Silence */
+               snd_emu1010_fpga_link_dst_src_write(emu, 0x0100 + i, EMU_SRC_SILENCE);
        }
-       for (i = 0;i < 4; i++) {
+       for (i = 0; i < 4; i++) {
                /* Hana SPDIF Out <- Silence */
-               snd_emu1010_fpga_link_dst_src_write(emu, 0x0200+i, EMU_SRC_SILENCE);
+               snd_emu1010_fpga_link_dst_src_write(emu, 0x0200 + i, EMU_SRC_SILENCE);
        }
-       for (i = 0;i < 7; i++) {
+       for (i = 0; i < 7; i++) {
                /* Hamoa DAC <- Silence */
-               snd_emu1010_fpga_link_dst_src_write(emu, 0x0300+i, EMU_SRC_SILENCE);
+               snd_emu1010_fpga_link_dst_src_write(emu, 0x0300 + i, EMU_SRC_SILENCE);
        }
-       for (i = 0;i < 7; i++) {
+       for (i = 0; i < 7; i++) {
                /* Hana ADAT Out <- Silence */
                snd_emu1010_fpga_link_dst_src_write(emu, EMU_DST_HANA_ADAT + i, EMU_SRC_SILENCE);
        }
@@ -1065,30 +1067,30 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
                EMU_DST_ALICE_I2S2_LEFT, EMU_SRC_DOCK_ADC3_LEFT1);
        snd_emu1010_fpga_link_dst_src_write(emu,
                EMU_DST_ALICE_I2S2_RIGHT, EMU_SRC_DOCK_ADC3_RIGHT1);
-       snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x01 ); // Unmute all
+       snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x01); /* Unmute all */
+
+       snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp);
 
-       snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp );
-       
        /* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
         * Lock Sound Memory Cache, Lock Tank Memory Cache,
         * Mute all codecs.
         */
-       outl(0x0000a000, emu->port + HCFG); 
+       outl(0x0000a000, emu->port + HCFG);
        /* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
         * Lock Sound Memory Cache, Lock Tank Memory Cache,
         * Un-Mute all codecs.
         */
        outl(0x0000a001, emu->port + HCFG);
+
        /* Initial boot complete. Now patches */
 
-       snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp );
-       snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 ); /* MIDI Route */
-       snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c ); /* Unknown */
-       snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 ); /* MIDI Route */
-       snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c ); /* Unknown */
-       snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp ); 
-       snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); /* SPDIF Format spdif  (or 0x11 for aes/ebu) */
+       snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp);
+       snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19); /* MIDI Route */
+       snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c); /* Unknown */
+       snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19); /* MIDI Route */
+       snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c); /* Unknown */
+       snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp);
+       snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10); /* SPDIF Format spdif  (or 0x11 for aes/ebu) */
 
        /* Start Micro/Audio Dock firmware loader thread */
        if (!emu->emu1010.firmware_thread) {
@@ -1218,20 +1220,20 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
                emu->emu1010.output_source[23] = 28;
        }
        /* TEMP: Select SPDIF in/out */
-       //snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); /* Output spdif */
+       /* snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); */ /* Output spdif */
 
        /* TEMP: Select 48kHz SPDIF out */
        snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x0); /* Mute all */
        snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x0); /* Default fallback clock 48kHz */
        /* Word Clock source, Internal 48kHz x1 */
-       snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K );
-       //snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X );
+       snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K);
+       /* snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X); */
        emu->emu1010.internal_clock = 1; /* 48000 */
-       snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);/* Set LEDs on Audio Dock */
+       snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12); /* Set LEDs on Audio Dock */
        snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x1); /* Unmute all */
-       //snd_emu1010_fpga_write(emu, 0x7, 0x0); /* Mute all */
-       //snd_emu1010_fpga_write(emu, 0x7, 0x1); /* Unmute all */
-       //snd_emu1010_fpga_write(emu, 0xe, 0x12); /* Set LEDs on Audio Dock */
+       /* snd_emu1010_fpga_write(emu, 0x7, 0x0); */ /* Mute all */
+       /* snd_emu1010_fpga_write(emu, 0x7, 0x1); */ /* Unmute all */
+       /* snd_emu1010_fpga_write(emu, 0xe, 0x12); */ /* Set LEDs on Audio Dock */
 
        return 0;
 }
@@ -1247,13 +1249,13 @@ static void free_pm_buffer(struct snd_emu10k1 *emu);
 static int snd_emu10k1_free(struct snd_emu10k1 *emu)
 {
        if (emu->port) {        /* avoid access to already used hardware */
-               snd_emu10k1_fx8010_tram_setup(emu, 0);
+               snd_emu10k1_fx8010_tram_setup(emu, 0);
                snd_emu10k1_done(emu);
                snd_emu10k1_free_efx(emu);
-               }
+       }
        if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) {
                /* Disable 48Volt power to Audio Dock */
-               snd_emu1010_fpga_write(emu,  EMU_HANA_DOCK_PWR,  0 );
+               snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
        }
        if (emu->emu1010.firmware_thread)
                kthread_stop(emu->emu1010.firmware_thread);
@@ -1278,7 +1280,7 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu)
 #endif
        if (emu->port)
                pci_release_regions(emu->pci);
-       if (emu->card_capabilities->ca0151_chip) /* P16V */     
+       if (emu->card_capabilities->ca0151_chip) /* P16V */
                snd_p16v_free(emu);
        pci_disable_device(emu->pci);
        kfree(emu);
@@ -1292,21 +1294,6 @@ static int snd_emu10k1_dev_free(struct snd_device *device)
 }
 
 static struct snd_emu_chip_details emu_chip_details[] = {
-       /* Audigy 2 Value AC3 out does not work yet. Need to find out how to turn off interpolators.*/
-       /* Tested by James@superbug.co.uk 3rd July 2005 */
-       /* DSP: CA0108-IAT
-        * DAC: CS4382-KQ
-        * ADC: Philips 1361T
-        * AC97: STAC9750
-        * CA0151: None
-        */
-       {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102,
-        .driver = "Audigy2", .name = "Audigy 2 Value [SB0400]", 
-        .id = "Audigy2",
-        .emu10k2_chip = 1,
-        .ca0108_chip = 1,
-        .spk71 = 1,
-        .ac97_chip = 1} ,
        /* Audigy4 (Not PRO) SB0610 */
        /* Tested by James@superbug.co.uk 4th April 2006 */
        /* A_IOCFG bits
@@ -1346,20 +1333,37 @@ static struct snd_emu_chip_details emu_chip_details[] = {
         * CA0151: None
         */
        {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10211102,
-        .driver = "Audigy2", .name = "Audigy 4 [SB0610]", 
+        .driver = "Audigy2", .name = "SB Audigy 4 [SB0610]",
         .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0108_chip = 1,
         .spk71 = 1,
         .adc_1361t = 1,  /* 24 bit capture instead of 16bit */
         .ac97_chip = 1} ,
+       /* Audigy 2 Value AC3 out does not work yet.
+        * Need to find out how to turn off interpolators.
+        */
+       /* Tested by James@superbug.co.uk 3rd July 2005 */
+       /* DSP: CA0108-IAT
+        * DAC: CS4382-KQ
+        * ADC: Philips 1361T
+        * AC97: STAC9750
+        * CA0151: None
+        */
+       {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102,
+        .driver = "Audigy2", .name = "SB Audigy 2 Value [SB0400]",
+        .id = "Audigy2",
+        .emu10k2_chip = 1,
+        .ca0108_chip = 1,
+        .spk71 = 1,
+        .ac97_chip = 1} ,
        /* Audigy 2 ZS Notebook Cardbus card.*/
        /* Tested by James@superbug.co.uk 6th November 2006 */
        /* Audio output 7.1/Headphones working.
         * Digital output working. (AC3 not checked, only PCM)
         * Audio Mic/Line inputs working.
         * Digital input not tested.
-        */ 
+        */
        /* DSP: Tina2
         * DAC: Wolfson WM8768/WM8568
         * ADC: Wolfson WM8775
@@ -1386,7 +1390,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
         *
         */
        {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x20011102,
-        .driver = "Audigy2", .name = "Audigy 2 ZS Notebook [SB0530]", 
+        .driver = "Audigy2", .name = "SB Audigy 2 ZS Notebook [SB0530]",
         .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0108_chip = 1,
@@ -1396,7 +1400,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
         .spk71 = 1} ,
        /* Tested by James@superbug.co.uk 4th Nov 2007. */
        {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x42011102,
-        .driver = "Audigy2", .name = "E-mu 1010 Notebook [MAEM8950]", 
+        .driver = "Audigy2", .name = "E-mu 1010 Notebook [MAEM8950]",
         .id = "EMU1010",
         .emu10k2_chip = 1,
         .ca0108_chip = 1,
@@ -1404,47 +1408,49 @@ static struct snd_emu_chip_details emu_chip_details[] = {
         .spk71 = 1 ,
         .emu_model = EMU_MODEL_EMU1616},
        /* Tested by James@superbug.co.uk 4th Nov 2007. */
+       /* This is MAEM8960, 0202 is MAEM 8980 */
        {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102,
-        .driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM????]", 
+        .driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM8960]",
         .id = "EMU1010",
         .emu10k2_chip = 1,
         .ca0108_chip = 1,
         .spk71 = 1,
-        .emu_model = EMU_MODEL_EMU1010B},
+        .emu_model = EMU_MODEL_EMU1010B}, /* EMU 1010 new revision */
        /* Tested by James@superbug.co.uk 8th July 2005. */
+       /* This is MAEM8810, 0202 is MAEM8820 */
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102,
-        .driver = "Audigy2", .name = "E-mu 1010 [4001]",
+        .driver = "Audigy2", .name = "E-mu 1010 [MAEM8810]",
         .id = "EMU1010",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
         .spk71 = 1,
-        .emu_model = EMU_MODEL_EMU1010}, /* Emu 1010 */
+        .emu_model = EMU_MODEL_EMU1010}, /* EMU 1010 old revision */
        /* EMU0404b */
        {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40021102,
-        .driver = "Audigy2", .name = "E-mu 0404b [4002]",
+        .driver = "Audigy2", .name = "E-mu 0404b PCI [MAEM8852]",
         .id = "EMU0404",
         .emu10k2_chip = 1,
         .ca0108_chip = 1,
         .spk71 = 1,
-        .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */
+        .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 new revision */
        /* Tested by James@superbug.co.uk 20-3-2007. */
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40021102,
-        .driver = "Audigy2", .name = "E-mu 0404 [4002]",
+        .driver = "Audigy2", .name = "E-mu 0404 [MAEM8850]",
         .id = "EMU0404",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
         .spk71 = 1,
         .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */
-       /* Audigy4 (Not PRO) SB0610 */
-       {.vendor = 0x1102, .device = 0x0008, 
-        .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", 
+       /* Note that all E-mu cards require kernel 2.6 or newer. */
+       {.vendor = 0x1102, .device = 0x0008,
+        .driver = "Audigy2", .name = "SB Audigy 2 Value [Unknown]",
         .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0108_chip = 1,
         .ac97_chip = 1} ,
        /* Tested by James@superbug.co.uk 3rd July 2005 */
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102,
-        .driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]", 
+        .driver = "Audigy2", .name = "SB Audigy 4 PRO [SB0380]",
         .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
@@ -1457,7 +1463,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
         * Just like 0x20021102
         */
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20061102,
-        .driver = "Audigy2", .name = "Audigy 2 [SB0350b]", 
+        .driver = "Audigy2", .name = "SB Audigy 2 [SB0350b]",
         .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
@@ -1467,7 +1473,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
         .invert_shared_spdif = 1,      /* digital/analog switch swapped */
         .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20021102,
-        .driver = "Audigy2", .name = "Audigy 2 ZS [SB0350]", 
+        .driver = "Audigy2", .name = "SB Audigy 2 ZS [SB0350]",
         .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
@@ -1477,7 +1483,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
         .invert_shared_spdif = 1,      /* digital/analog switch swapped */
         .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20011102,
-        .driver = "Audigy2", .name = "Audigy 2 ZS [2001]", 
+        .driver = "Audigy2", .name = "SB Audigy 2 ZS [SB0360]",
         .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
@@ -1495,7 +1501,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
         * CA0151: Yes
         */
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10071102,
-        .driver = "Audigy2", .name = "Audigy 2 [SB0240]", 
+        .driver = "Audigy2", .name = "SB Audigy 2 [SB0240]",
         .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
@@ -1505,7 +1511,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
         .adc_1361t = 1,  /* 24 bit capture instead of 16bit */
         .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102,
-        .driver = "Audigy2", .name = "Audigy 2 EX [1005]", 
+        .driver = "Audigy2", .name = "SB Audigy 2 Platinum EX [SB0280]",
         .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
@@ -1515,7 +1521,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
        /* Dell OEM/Creative Labs Audigy 2 ZS */
        /* See ALSA bug#1365 */
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10031102,
-        .driver = "Audigy2", .name = "Audigy 2 ZS [SB0353]",
+        .driver = "Audigy2", .name = "SB Audigy 2 ZS [SB0353]",
         .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
@@ -1524,7 +1530,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
         .spdif_bug = 1,
         .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10021102,
-        .driver = "Audigy2", .name = "Audigy 2 Platinum [SB0240P]", 
+        .driver = "Audigy2", .name = "SB Audigy 2 Platinum [SB0240P]",
         .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
@@ -1535,7 +1541,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
         .adc_1361t = 1,  /* 24 bit capture instead of 16bit. Fixes ALSA bug#324 */
         .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0004, .revision = 0x04,
-        .driver = "Audigy2", .name = "Audigy 2 [Unknown]",
+        .driver = "Audigy2", .name = "SB Audigy 2 [Unknown]",
         .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
@@ -1543,78 +1549,79 @@ static struct snd_emu_chip_details emu_chip_details[] = {
         .spdif_bug = 1,
         .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00531102,
-        .driver = "Audigy", .name = "Audigy 1 [SB0090]", 
+        .driver = "Audigy", .name = "SB Audigy 1 [SB0092]",
         .id = "Audigy",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
         .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00521102,
-        .driver = "Audigy", .name = "Audigy 1 ES [SB0160]", 
+        .driver = "Audigy", .name = "SB Audigy 1 ES [SB0160]",
         .id = "Audigy",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
         .spdif_bug = 1,
         .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00511102,
-        .driver = "Audigy", .name = "Audigy 1 [SB0090]", 
+        .driver = "Audigy", .name = "SB Audigy 1 [SB0090]",
         .id = "Audigy",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
         .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0004,
-        .driver = "Audigy", .name = "Audigy 1 [Unknown]", 
+        .driver = "Audigy", .name = "Audigy 1 [Unknown]",
         .id = "Audigy",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
         .ac97_chip = 1} ,
-       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806B1102,
-        .driver = "EMU10K1", .name = "SBLive! [SB0105]", 
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x100a1102,
+        .driver = "EMU10K1", .name = "SB Live! 5.1 [SB0220]",
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806b1102,
+        .driver = "EMU10K1", .name = "SB Live! [SB0105]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
-       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806A1102,
-        .driver = "EMU10K1", .name = "SBLive! Value [SB0103]", 
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806a1102,
+        .driver = "EMU10K1", .name = "SB Live! Value [SB0103]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80691102,
-        .driver = "EMU10K1", .name = "SBLive! Value [SB0101]", 
+        .driver = "EMU10K1", .name = "SB Live! Value [SB0101]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
        /* Tested by ALSA bug#1680 26th December 2005 */
-       /* note: It really has SB0220 written on the card. */
+       /* note: It really has SB0220 written on the card, */
+       /* but it's SB0228 according to kx.inf */
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80661102,
-        .driver = "EMU10K1", .name = "SB Live 5.1 Dell OEM [SB0220]", 
+        .driver = "EMU10K1", .name = "SB Live! 5.1 Dell OEM [SB0228]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
        /* Tested by Thomas Zehetbauer 27th Aug 2005 */
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80651102,
-        .driver = "EMU10K1", .name = "SB Live 5.1 [SB0220]", 
-        .id = "Live",
-        .emu10k1_chip = 1,
-        .ac97_chip = 1,
-        .sblive51 = 1} ,
-       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x100a1102,
-        .driver = "EMU10K1", .name = "SB Live 5.1 [SB0220]", 
+        .driver = "EMU10K1", .name = "SB Live! 5.1 [SB0220]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80641102,
-        .driver = "EMU10K1", .name = "SB Live 5.1", 
+        .driver = "EMU10K1", .name = "SB Live! 5.1",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
        /* Tested by alsa bugtrack user "hus" bug #1297 12th Aug 2005 */
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102,
-        .driver = "EMU10K1", .name = "SBLive 5.1 [SB0060]",
+        .driver = "EMU10K1", .name = "SB Live! 5.1 [SB0060]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 2, /* ac97 is optional; both SBLive 5.1 and platinum
@@ -1622,78 +1629,78 @@ static struct snd_emu_chip_details emu_chip_details[] = {
                          */
         .sblive51 = 1} ,
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80511102,
-        .driver = "EMU10K1", .name = "SBLive! Value [CT4850]", 
+        .driver = "EMU10K1", .name = "SB Live! Value [CT4850]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102,
-        .driver = "EMU10K1", .name = "SBLive! Platinum [CT4760P]", 
+        .driver = "EMU10K1", .name = "SB Live! Platinum [CT4760P]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80321102,
-        .driver = "EMU10K1", .name = "SBLive! Value [CT4871]", 
+        .driver = "EMU10K1", .name = "SB Live! Value [CT4871]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80311102,
-        .driver = "EMU10K1", .name = "SBLive! Value [CT4831]", 
+        .driver = "EMU10K1", .name = "SB Live! Value [CT4831]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80281102,
-        .driver = "EMU10K1", .name = "SBLive! Value [CT4870]", 
+        .driver = "EMU10K1", .name = "SB Live! Value [CT4870]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
        /* Tested by James@superbug.co.uk 3rd July 2005 */
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80271102,
-        .driver = "EMU10K1", .name = "SBLive! Value [CT4832]", 
+        .driver = "EMU10K1", .name = "SB Live! Value [CT4832]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80261102,
-        .driver = "EMU10K1", .name = "SBLive! Value [CT4830]", 
+        .driver = "EMU10K1", .name = "SB Live! Value [CT4830]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80231102,
-        .driver = "EMU10K1", .name = "SB PCI512 [CT4790]", 
+        .driver = "EMU10K1", .name = "SB PCI512 [CT4790]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80221102,
-        .driver = "EMU10K1", .name = "SBLive! Value [CT4780]", 
+        .driver = "EMU10K1", .name = "SB Live! Value [CT4780]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x40011102,
-        .driver = "EMU10K1", .name = "E-mu APS [4001]", 
+        .driver = "EMU10K1", .name = "E-mu APS [PC545]",
         .id = "APS",
         .emu10k1_chip = 1,
         .ecard = 1} ,
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00211102,
-        .driver = "EMU10K1", .name = "SBLive! [CT4620]", 
+        .driver = "EMU10K1", .name = "SB Live! [CT4620]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00201102,
-        .driver = "EMU10K1", .name = "SBLive! Value [CT4670]", 
+        .driver = "EMU10K1", .name = "SB Live! Value [CT4670]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
         .sblive51 = 1} ,
        {.vendor = 0x1102, .device = 0x0002,
-        .driver = "EMU10K1", .name = "SB Live [Unknown]", 
+        .driver = "EMU10K1", .name = "SB Live! [Unknown]",
         .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1,
@@ -1702,13 +1709,13 @@ static struct snd_emu_chip_details emu_chip_details[] = {
 };
 
 int __devinit snd_emu10k1_create(struct snd_card *card,
-                      struct pci_dev * pci,
+                      struct pci_dev *pci,
                       unsigned short extin_mask,
                       unsigned short extout_mask,
                       long max_cache_bytes,
                       int enable_ir,
                       uint subsystem,
-                      struct snd_emu10k1 ** remu)
+                      struct snd_emu10k1 **remu)
 {
        struct snd_emu10k1 *emu;
        int idx, err;
@@ -1718,11 +1725,12 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
        static struct snd_device_ops ops = {
                .dev_free =     snd_emu10k1_dev_free,
        };
-       
+
        *remu = NULL;
 
        /* enable PCI device */
-       if ((err = pci_enable_device(pci)) < 0)
+       err = pci_enable_device(pci);
+       if (err < 0)
                return err;
 
        emu = kzalloc(sizeof(*emu), GFP_KERNEL);
@@ -1749,16 +1757,17 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
        emu->revision = pci->revision;
        pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);
        pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &emu->model);
-       snd_printdd("vendor=0x%x, device=0x%x, subsystem_vendor_id=0x%x, subsystem_id=0x%x\n",pci->vendor, pci->device, emu->serial, emu->model);
+       snd_printdd("vendor = 0x%x, device = 0x%x, subsystem_vendor_id = 0x%x, subsystem_id = 0x%x\n", pci->vendor, pci->device, emu->serial, emu->model);
 
        for (c = emu_chip_details; c->vendor; c++) {
                if (c->vendor == pci->vendor && c->device == pci->device) {
                        if (subsystem) {
-                               if (c->subsystem && (c->subsystem == subsystem) ) {
+                               if (c->subsystem && (c->subsystem == subsystem))
                                        break;
-                               } else continue;
+                               else
+                                       continue;
                        } else {
-                               if (c->subsystem && (c->subsystem != emu->serial) )
+                               if (c->subsystem && (c->subsystem != emu->serial))
                                        continue;
                                if (c->revision && c->revision != emu->revision)
                                        continue;
@@ -1774,14 +1783,18 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
        }
        emu->card_capabilities = c;
        if (c->subsystem && !subsystem)
-               snd_printdd("Sound card name=%s\n", c->name);
-       else if (subsystem) 
-               snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x. Forced to subsytem=0x%x\n",
-                       c->name, pci->vendor, pci->device, emu->serial, c->subsystem);
-       else 
-               snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x.\n",
-                       c->name, pci->vendor, pci->device, emu->serial);
-       
+               snd_printdd("Sound card name = %s\n", c->name);
+       else if (subsystem)
+               snd_printdd("Sound card name = %s, "
+                       "vendor = 0x%x, device = 0x%x, subsystem = 0x%x. "
+                       "Forced to subsytem = 0x%x\n",  c->name,
+                       pci->vendor, pci->device, emu->serial, c->subsystem);
+       else
+               snd_printdd("Sound card name = %s, "
+                       "vendor = 0x%x, device = 0x%x, subsystem = 0x%x.\n",
+                       c->name, pci->vendor, pci->device,
+                       emu->serial);
+
        if (!*card->id && c->id) {
                int i, n = 0;
                strlcpy(card->id, c->id, sizeof(card->id));
@@ -1815,7 +1828,8 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
        else
                emu->gpr_base = FXGPREGBASE;
 
-       if ((err = pci_request_regions(pci, "EMU10K1")) < 0) {
+       err = pci_request_regions(pci, "EMU10K1");
+       if (err < 0) {
                kfree(emu);
                pci_disable_device(pci);
                return err;
@@ -1862,21 +1876,25 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
        emu->enable_ir = enable_ir;
 
        if (emu->card_capabilities->ca_cardbus_chip) {
-               if ((err = snd_emu10k1_cardbus_init(emu)) < 0)
+               err = snd_emu10k1_cardbus_init(emu);
+               if (err < 0)
                        goto error;
        }
        if (emu->card_capabilities->ecard) {
-               if ((err = snd_emu10k1_ecard_init(emu)) < 0)
+               err = snd_emu10k1_ecard_init(emu);
+               if (err < 0)
                        goto error;
        } else if (emu->card_capabilities->emu_model) {
-               if ((err = snd_emu10k1_emu1010_init(emu)) < 0) {
-                       snd_emu10k1_free(emu);
-                       return err;
-               }
+               err = snd_emu10k1_emu1010_init(emu);
+               if (err < 0) {
+                       snd_emu10k1_free(emu);
+                       return err;
+               }
        } else {
                /* 5.1: Enable the additional AC97 Slots. If the emu10k1 version
                        does not support this, it shouldn't do any harm */
-               snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
+               snd_emu10k1_ptr_write(emu, AC97SLOT, 0,
+                                       AC97SLOT_CNTR|AC97SLOT_LFE);
        }
 
        /* initialize TRAM setup */
@@ -1916,7 +1934,7 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
                snd_emu10k1_synth_alloc(emu, 4096);
        if (emu->reserved_page)
                emu->reserved_page->map_locked = 1;
-       
+
        /* Clear silent pages and set up pointers */
        memset(emu->silent_page.area, 0, PAGE_SIZE);
        silent_page = emu->silent_page.addr << 1;
@@ -1929,19 +1947,23 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
                emu->voices[idx].number = idx;
        }
 
-       if ((err = snd_emu10k1_init(emu, enable_ir, 0)) < 0)
+       err = snd_emu10k1_init(emu, enable_ir, 0);
+       if (err < 0)
                goto error;
 #ifdef CONFIG_PM
-       if ((err = alloc_pm_buffer(emu)) < 0)
+       err = alloc_pm_buffer(emu);
+       if (err < 0)
                goto error;
 #endif
 
        /*  Initialize the effect engine */
-       if ((err = snd_emu10k1_init_efx(emu)) < 0)
+       err = snd_emu10k1_init_efx(emu);
+       if (err < 0)
                goto error;
        snd_emu10k1_audio_enable(emu);
 
-       if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, emu, &ops)) < 0)
+       err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, emu, &ops);
+       if (err < 0)
                goto error;
 
 #ifdef CONFIG_PROC_FS
@@ -1981,7 +2003,7 @@ static int __devinit alloc_pm_buffer(struct snd_emu10k1 *emu)
        if (emu->audigy)
                size += ARRAY_SIZE(saved_regs_audigy);
        emu->saved_ptr = vmalloc(4 * NUM_G * size);
-       if (! emu->saved_ptr)
+       if (!emu->saved_ptr)
                return -ENOMEM;
        if (snd_emu10k1_efx_alloc_pm_buffer(emu) < 0)
                return -ENOMEM;
@@ -2026,7 +2048,7 @@ void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
        if (emu->card_capabilities->ecard)
                snd_emu10k1_ecard_init(emu);
        else if (emu->card_capabilities->emu_model)
-               snd_emu10k1_emu1010_init(emu);
+               snd_emu10k1_emu1010_init(emu);
        else
                snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
        snd_emu10k1_init(emu, emu->enable_ir, 1);
index f34bbfb..b0fb6c9 100644 (file)
@@ -1639,6 +1639,45 @@ static struct snd_kcontrol_new snd_audigy_shared_spdif __devinitdata =
        .put =          snd_emu10k1_shared_spdif_put
 };
 
+/* workaround for too low volume on Audigy due to 16bit/24bit conversion */
+
+#define snd_audigy_capture_boost_info  snd_ctl_boolean_mono_info
+
+static int snd_audigy_capture_boost_get(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int val;
+
+       /* FIXME: better to use a cached version */
+       val = snd_ac97_read(emu->ac97, AC97_REC_GAIN);
+       ucontrol->value.integer.value[0] = !!val;
+       return 0;
+}
+
+static int snd_audigy_capture_boost_put(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int val;
+
+       if (ucontrol->value.integer.value[0])
+               val = 0x0f0f;
+       else
+               val = 0;
+       return snd_ac97_update(emu->ac97, AC97_REC_GAIN, val);
+}
+
+static struct snd_kcontrol_new snd_audigy_capture_boost __devinitdata =
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name =         "Analog Capture Boost",
+       .info =         snd_audigy_capture_boost_info,
+       .get =          snd_audigy_capture_boost_get,
+       .put =          snd_audigy_capture_boost_put
+};
+
+
 /*
  */
 static void snd_emu10k1_mixer_free_ac97(struct snd_ac97 *ac97)
@@ -2087,5 +2126,12 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
                }
        }
                
+       if (emu->card_capabilities->ac97_chip && emu->audigy) {
+               err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_capture_boost,
+                                                    emu));
+               if (err < 0)
+                       return err;
+       }
+
        return 0;
 }
index 20ee759..e9c3794 100644 (file)
@@ -1953,7 +1953,7 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id)
        outw(inw(chip->io_port + 4) & 1, chip->io_port + 4);
 
        if (event & ESM_HWVOL_IRQ)
-               tasklet_hi_schedule(&chip->hwvol_tq); /* we'll do this later */
+               tasklet_schedule(&chip->hwvol_tq); /* we'll do this later */
 
        /* else ack 'em all, i imagine */
        outb(0xFF, chip->io_port + 0x1A);
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
new file mode 100644 (file)
index 0000000..eb2a19b
--- /dev/null
@@ -0,0 +1,188 @@
+menuconfig SND_HDA_INTEL
+       tristate "Intel HD Audio"
+       select SND_PCM
+       select SND_VMASTER
+       select SND_JACK if INPUT=y || INPUT=SND
+       help
+         Say Y here to include support for Intel "High Definition
+         Audio" (Azalia) and its compatible devices.
+
+         This option enables the HD-audio controller.  Don't forget
+         to choose the appropriate codec options below.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-hda-intel.
+
+if SND_HDA_INTEL
+
+config SND_HDA_HWDEP
+       bool "Build hwdep interface for HD-audio driver"
+       select SND_HWDEP
+       help
+         Say Y here to build a hwdep interface for HD-audio driver.
+         This interface can be used for out-of-band communication
+         with codecs for debugging purposes.
+
+config SND_HDA_RECONFIG
+       bool "Allow dynamic codec reconfiguration (EXPERIMENTAL)"
+       depends on SND_HDA_HWDEP && EXPERIMENTAL
+       help
+         Say Y here to enable the HD-audio codec re-configuration feature.
+         This adds the sysfs interfaces to allow user to clear the whole
+         codec configuration, change the codec setup, add extra verbs,
+         and re-configure the codec dynamically.
+
+config SND_HDA_INPUT_BEEP
+       bool "Support digital beep via input layer"
+       depends on INPUT=y || INPUT=SND_HDA_INTEL
+       help
+         Say Y here to build a digital beep interface for HD-audio
+         driver. This interface is used to generate digital beeps.
+
+config SND_HDA_CODEC_REALTEK
+       bool "Build Realtek HD-audio codec support"
+       default y
+       help
+         Say Y here to include Realtek HD-audio codec support in
+         snd-hda-intel driver, such as ALC880.
+
+         When the HD-audio driver is built as a module, the codec
+         support code is also built as another module,
+         snd-hda-codec-realtek.
+         This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_ANALOG
+       bool "Build Analog Device HD-audio codec support"
+       default y
+       help
+         Say Y here to include Analog Device HD-audio codec support in
+         snd-hda-intel driver, such as AD1986A.
+
+         When the HD-audio driver is built as a module, the codec
+         support code is also built as another module,
+         snd-hda-codec-analog.
+         This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_SIGMATEL
+       bool "Build IDT/Sigmatel HD-audio codec support"
+       default y
+       help
+         Say Y here to include IDT (Sigmatel) HD-audio codec support in
+         snd-hda-intel driver, such as STAC9200.
+
+         When the HD-audio driver is built as a module, the codec
+         support code is also built as another module,
+         snd-hda-codec-idt.
+         This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_VIA
+       bool "Build VIA HD-audio codec support"
+       default y
+       help
+         Say Y here to include VIA HD-audio codec support in
+         snd-hda-intel driver, such as VT1708.
+
+         When the HD-audio driver is built as a module, the codec
+         support code is also built as another module,
+         snd-hda-codec-via.
+         This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_ATIHDMI
+       bool "Build ATI HDMI HD-audio codec support"
+       default y
+       help
+         Say Y here to include ATI HDMI HD-audio codec support in
+         snd-hda-intel driver, such as ATI RS600 HDMI.
+
+         When the HD-audio driver is built as a module, the codec
+         support code is also built as another module,
+         snd-hda-codec-atihdmi.
+         This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_NVHDMI
+       bool "Build NVIDIA HDMI HD-audio codec support"
+       default y
+       help
+         Say Y here to include NVIDIA HDMI HD-audio codec support in
+         snd-hda-intel driver, such as NVIDIA MCP78 HDMI.
+
+         When the HD-audio driver is built as a module, the codec
+         support code is also built as another module,
+         snd-hda-codec-nvhdmi.
+         This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_INTELHDMI
+       bool "Build INTEL HDMI HD-audio codec support"
+       default y
+       help
+         Say Y here to include INTEL HDMI HD-audio codec support in
+         snd-hda-intel driver, such as Eaglelake integrated HDMI.
+
+         When the HD-audio driver is built as a module, the codec
+         support code is also built as another module,
+         snd-hda-codec-intelhdmi.
+         This module is automatically loaded at probing.
+
+config SND_HDA_ELD
+       def_bool y
+       depends on SND_HDA_CODEC_INTELHDMI
+
+config SND_HDA_CODEC_CONEXANT
+       bool "Build Conexant HD-audio codec support"
+       default y
+       help
+         Say Y here to include Conexant HD-audio codec support in
+         snd-hda-intel driver, such as CX20549.
+
+         When the HD-audio driver is built as a module, the codec
+         support code is also built as another module,
+         snd-hda-codec-conexant.
+         This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_CMEDIA
+       bool "Build C-Media HD-audio codec support"
+       default y
+       help
+         Say Y here to include C-Media HD-audio codec support in
+         snd-hda-intel driver, such as CMI9880.
+
+         When the HD-audio driver is built as a module, the codec
+         support code is also built as another module,
+         snd-hda-codec-cmedia.
+         This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_SI3054
+       bool "Build Silicon Labs 3054 HD-modem codec support"
+       default y
+       help
+         Say Y here to include Silicon Labs 3054 HD-modem codec
+         (and compatibles) support in snd-hda-intel driver.
+
+         When the HD-audio driver is built as a module, the codec
+         support code is also built as another module,
+         snd-hda-codec-si3054.
+         This module is automatically loaded at probing.
+
+config SND_HDA_GENERIC
+       bool "Enable generic HD-audio codec parser"
+       default y
+       help
+         Say Y here to enable the generic HD-audio codec parser
+         in snd-hda-intel driver.
+
+config SND_HDA_POWER_SAVE
+       bool "Aggressive power-saving on HD-audio"
+       help
+         Say Y here to enable more aggressive power-saving mode on
+         HD-audio driver.  The power-saving timeout can be configured
+         via power_save option or over sysfs on-the-fly.
+
+config SND_HDA_POWER_SAVE_DEFAULT
+       int "Default time-out for HD-audio power-save mode"
+       depends on SND_HDA_POWER_SAVE
+       default 0
+       help
+         The default time-out value in seconds for HD-audio automatic
+         power-save mode.  0 means to disable the power-save mode.
+
+endif
index 1980c6d..50f9d09 100644 (file)
@@ -1,20 +1,59 @@
-snd-hda-intel-y := hda_intel.o
-# since snd-hda-intel is the only driver using hda-codec,
-# merge it into a single module although it was originally
-# designed to be individual modules
-snd-hda-intel-y += hda_codec.o
-snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o
-snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
-snd-hda-intel-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
-snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_REALTEK) += patch_realtek.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CMEDIA) += patch_cmedia.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ANALOG) += patch_analog.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += patch_sigmatel.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SI3054) += patch_si3054.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ATIHDMI) += patch_atihdmi.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CONEXANT) += patch_conexant.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_VIA) += patch_via.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_NVHDMI) += patch_nvhdmi.o
+snd-hda-intel-objs := hda_intel.o
 
+snd-hda-codec-y := hda_codec.o
+snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
+snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
+# snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o
+snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
+snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
+
+snd-hda-codec-realtek-objs :=  patch_realtek.o
+snd-hda-codec-cmedia-objs :=   patch_cmedia.o
+snd-hda-codec-analog-objs :=   patch_analog.o
+snd-hda-codec-idt-objs :=      patch_sigmatel.o
+snd-hda-codec-si3054-objs :=   patch_si3054.o
+snd-hda-codec-atihdmi-objs :=  patch_atihdmi.o
+snd-hda-codec-conexant-objs := patch_conexant.o
+snd-hda-codec-via-objs :=      patch_via.o
+snd-hda-codec-nvhdmi-objs :=   patch_nvhdmi.o
+snd-hda-codec-intelhdmi-objs :=        patch_intelhdmi.o hda_eld.o
+
+# common driver
+obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o
+
+# codec drivers (note: CONFIG_SND_HDA_CODEC_XXX are booleans)
+ifdef CONFIG_SND_HDA_CODEC_REALTEK
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-realtek.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_CMEDIA
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cmedia.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_ANALOG
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-analog.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_SIGMATEL
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-idt.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_SI3054
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_CONEXANT
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_VIA
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_NVHDMI
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-nvhdmi.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_INTELHDMI
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-intelhdmi.o
+endif
+
+# this must be the last entry after codec drivers;
+# otherwise the codec patches won't be hooked before the PCI probe
+# when built in kernel
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
index 3ecd7e7..e00421c 100644 (file)
@@ -128,6 +128,7 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
        INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device);
 
 void snd_hda_detach_beep_device(struct hda_codec *codec)
 {
@@ -140,3 +141,4 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
                kfree(beep);
        }
 }
+EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
index eb91641..e16cf63 100644 (file)
 #include <sound/initval.h>
 #include "hda_local.h"
 #include <sound/hda_hwdep.h>
-#include "hda_patch.h" /* codec presets */
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-/* define this option here to hide as static */
-static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
-module_param(power_save, int, 0644);
-MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
-                "(in second, 0 = disable).");
-#endif
 
 /*
  * vendor / preset table
@@ -55,6 +46,7 @@ static struct hda_vendor_id hda_vendor_ids[] = {
        { 0x1002, "ATI" },
        { 0x1057, "Motorola" },
        { 0x1095, "Silicon Image" },
+       { 0x10de, "Nvidia" },
        { 0x10ec, "Realtek" },
        { 0x1106, "VIA" },
        { 0x111d, "IDT" },
@@ -66,40 +58,31 @@ static struct hda_vendor_id hda_vendor_ids[] = {
        { 0x1854, "LG" },
        { 0x1aec, "Wolfson Microelectronics" },
        { 0x434d, "C-Media" },
+       { 0x8086, "Intel" },
        { 0x8384, "SigmaTel" },
        {} /* terminator */
 };
 
-static const struct hda_codec_preset *hda_preset_tables[] = {
-#ifdef CONFIG_SND_HDA_CODEC_REALTEK
-       snd_hda_preset_realtek,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_CMEDIA
-       snd_hda_preset_cmedia,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_ANALOG
-       snd_hda_preset_analog,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_SIGMATEL
-       snd_hda_preset_sigmatel,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_SI3054
-       snd_hda_preset_si3054,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
-       snd_hda_preset_atihdmi,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_CONEXANT
-       snd_hda_preset_conexant,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_VIA
-       snd_hda_preset_via,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_NVHDMI
-       snd_hda_preset_nvhdmi,
-#endif
-       NULL
-};
+static DEFINE_MUTEX(preset_mutex);
+static LIST_HEAD(hda_preset_tables);
+
+int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset)
+{
+       mutex_lock(&preset_mutex);
+       list_add_tail(&preset->list, &hda_preset_tables);
+       mutex_unlock(&preset_mutex);
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_add_codec_preset);
+
+int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset)
+{
+       mutex_lock(&preset_mutex);
+       list_del(&preset->list);
+       mutex_unlock(&preset_mutex);
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset);
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static void hda_power_work(struct work_struct *work);
@@ -108,6 +91,72 @@ static void hda_keep_power_on(struct hda_codec *codec);
 static inline void hda_keep_power_on(struct hda_codec *codec) {}
 #endif
 
+const char *snd_hda_get_jack_location(u32 cfg)
+{
+       static char *bases[7] = {
+               "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
+       };
+       static unsigned char specials_idx[] = {
+               0x07, 0x08,
+               0x17, 0x18, 0x19,
+               0x37, 0x38
+       };
+       static char *specials[] = {
+               "Rear Panel", "Drive Bar",
+               "Riser", "HDMI", "ATAPI",
+               "Mobile-In", "Mobile-Out"
+       };
+       int i;
+       cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
+       if ((cfg & 0x0f) < 7)
+               return bases[cfg & 0x0f];
+       for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
+               if (cfg == specials_idx[i])
+                       return specials[i];
+       }
+       return "UNKNOWN";
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_jack_location);
+
+const char *snd_hda_get_jack_connectivity(u32 cfg)
+{
+       static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
+
+       return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity);
+
+const char *snd_hda_get_jack_type(u32 cfg)
+{
+       static char *jack_types[16] = {
+               "Line Out", "Speaker", "HP Out", "CD",
+               "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
+               "Line In", "Aux", "Mic", "Telephony",
+               "SPDIF In", "Digitial In", "Reserved", "Other"
+       };
+
+       return jack_types[(cfg & AC_DEFCFG_DEVICE)
+                               >> AC_DEFCFG_DEVICE_SHIFT];
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_jack_type);
+
+/*
+ * Compose a 32bit command word to be sent to the HD-audio controller
+ */
+static inline unsigned int
+make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
+              unsigned int verb, unsigned int parm)
+{
+       u32 val;
+
+       val = (u32)(codec->addr & 0x0f) << 28;
+       val |= (u32)direct << 27;
+       val |= (u32)nid << 20;
+       val |= verb << 8;
+       val |= parm;
+       return val;
+}
+
 /**
  * snd_hda_codec_read - send a command and get the response
  * @codec: the HDA codec
@@ -124,17 +173,21 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
                                int direct,
                                unsigned int verb, unsigned int parm)
 {
+       struct hda_bus *bus = codec->bus;
        unsigned int res;
+
+       res = make_codec_cmd(codec, nid, direct, verb, parm);
        snd_hda_power_up(codec);
-       mutex_lock(&codec->bus->cmd_mutex);
-       if (!codec->bus->ops.command(codec, nid, direct, verb, parm))
-               res = codec->bus->ops.get_response(codec);
+       mutex_lock(&bus->cmd_mutex);
+       if (!bus->ops.command(bus, res))
+               res = bus->ops.get_response(bus);
        else
                res = (unsigned int)-1;
-       mutex_unlock(&codec->bus->cmd_mutex);
+       mutex_unlock(&bus->cmd_mutex);
        snd_hda_power_down(codec);
        return res;
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_read);
 
 /**
  * snd_hda_codec_write - send a single command without waiting for response
@@ -151,14 +204,19 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
 int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
                         unsigned int verb, unsigned int parm)
 {
+       struct hda_bus *bus = codec->bus;
+       unsigned int res;
        int err;
+
+       res = make_codec_cmd(codec, nid, direct, verb, parm);
        snd_hda_power_up(codec);
-       mutex_lock(&codec->bus->cmd_mutex);
-       err = codec->bus->ops.command(codec, nid, direct, verb, parm);
-       mutex_unlock(&codec->bus->cmd_mutex);
+       mutex_lock(&bus->cmd_mutex);
+       err = bus->ops.command(bus, res);
+       mutex_unlock(&bus->cmd_mutex);
        snd_hda_power_down(codec);
        return err;
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_write);
 
 /**
  * snd_hda_sequence_write - sequence writes
@@ -173,6 +231,7 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq)
        for (; seq->nid; seq++)
                snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param);
 }
+EXPORT_SYMBOL_HDA(snd_hda_sequence_write);
 
 /**
  * snd_hda_get_sub_nodes - get the range of sub nodes
@@ -194,6 +253,7 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
        *start_id = (parm >> 16) & 0x7fff;
        return (int)(parm & 0x7fff);
 }
+EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
 
 /**
  * snd_hda_get_connections - get connection list
@@ -282,6 +342,7 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
        }
        return conns;
 }
+EXPORT_SYMBOL_HDA(snd_hda_get_connections);
 
 
 /**
@@ -316,6 +377,7 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
 
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_queue_unsol_event);
 
 /*
  * process queued unsolicited events
@@ -345,7 +407,7 @@ static void process_unsol_events(struct work_struct *work)
 /*
  * initialize unsolicited queue
  */
-static int __devinit init_unsol_queue(struct hda_bus *bus)
+static int init_unsol_queue(struct hda_bus *bus)
 {
        struct hda_bus_unsolicited *unsol;
 
@@ -391,9 +453,24 @@ static int snd_hda_bus_free(struct hda_bus *bus)
 static int snd_hda_bus_dev_free(struct snd_device *device)
 {
        struct hda_bus *bus = device->device_data;
+       bus->shutdown = 1;
        return snd_hda_bus_free(bus);
 }
 
+#ifdef CONFIG_SND_HDA_HWDEP
+static int snd_hda_bus_dev_register(struct snd_device *device)
+{
+       struct hda_bus *bus = device->device_data;
+       struct hda_codec *codec;
+       list_for_each_entry(codec, &bus->codec_list, list) {
+               snd_hda_hwdep_add_sysfs(codec);
+       }
+       return 0;
+}
+#else
+#define snd_hda_bus_dev_register       NULL
+#endif
+
 /**
  * snd_hda_bus_new - create a HDA bus
  * @card: the card entry
@@ -402,13 +479,14 @@ static int snd_hda_bus_dev_free(struct snd_device *device)
  *
  * Returns 0 if successful, or a negative error code.
  */
-int __devinit snd_hda_bus_new(struct snd_card *card,
+int /*__devinit*/ snd_hda_bus_new(struct snd_card *card,
                              const struct hda_bus_template *temp,
                              struct hda_bus **busp)
 {
        struct hda_bus *bus;
        int err;
        static struct snd_device_ops dev_ops = {
+               .dev_register = snd_hda_bus_dev_register,
                .dev_free = snd_hda_bus_dev_free,
        };
 
@@ -430,6 +508,7 @@ int __devinit snd_hda_bus_new(struct snd_card *card,
        bus->private_data = temp->private_data;
        bus->pci = temp->pci;
        bus->modelname = temp->modelname;
+       bus->power_save = temp->power_save;
        bus->ops = temp->ops;
 
        mutex_init(&bus->cmd_mutex);
@@ -444,27 +523,42 @@ int __devinit snd_hda_bus_new(struct snd_card *card,
                *busp = bus;
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_bus_new);
 
 #ifdef CONFIG_SND_HDA_GENERIC
 #define is_generic_config(codec) \
-       (codec->bus->modelname && !strcmp(codec->bus->modelname, "generic"))
+       (codec->modelname && !strcmp(codec->modelname, "generic"))
 #else
 #define is_generic_config(codec)       0
 #endif
 
+#ifdef MODULE
+#define HDA_MODREQ_MAX_COUNT   2       /* two request_modules()'s */
+#else
+#define HDA_MODREQ_MAX_COUNT   0       /* all presets are statically linked */
+#endif
+
 /*
  * find a matching codec preset
  */
-static const struct hda_codec_preset __devinit *
+static const struct hda_codec_preset *
 find_codec_preset(struct hda_codec *codec)
 {
-       const struct hda_codec_preset **tbl, *preset;
+       struct hda_codec_preset_list *tbl;
+       const struct hda_codec_preset *preset;
+       int mod_requested = 0;
 
        if (is_generic_config(codec))
                return NULL; /* use the generic parser */
 
-       for (tbl = hda_preset_tables; *tbl; tbl++) {
-               for (preset = *tbl; preset->id; preset++) {
+ again:
+       mutex_lock(&preset_mutex);
+       list_for_each_entry(tbl, &hda_preset_tables, list) {
+               if (!try_module_get(tbl->owner)) {
+                       snd_printk(KERN_ERR "hda_codec: cannot module_get\n");
+                       continue;
+               }
+               for (preset = tbl->preset; preset->id; preset++) {
                        u32 mask = preset->mask;
                        if (preset->afg && preset->afg != codec->afg)
                                continue;
@@ -474,23 +568,40 @@ find_codec_preset(struct hda_codec *codec)
                                mask = ~0;
                        if (preset->id == (codec->vendor_id & mask) &&
                            (!preset->rev ||
-                            preset->rev == codec->revision_id))
+                            preset->rev == codec->revision_id)) {
+                               mutex_unlock(&preset_mutex);
+                               codec->owner = tbl->owner;
                                return preset;
+                       }
                }
+               module_put(tbl->owner);
+       }
+       mutex_unlock(&preset_mutex);
+
+       if (mod_requested < HDA_MODREQ_MAX_COUNT) {
+               char name[32];
+               if (!mod_requested)
+                       snprintf(name, sizeof(name), "snd-hda-codec-id:%08x",
+                                codec->vendor_id);
+               else
+                       snprintf(name, sizeof(name), "snd-hda-codec-id:%04x*",
+                                (codec->vendor_id >> 16) & 0xffff);
+               request_module(name);
+               mod_requested++;
+               goto again;
        }
        return NULL;
 }
 
 /*
- * snd_hda_get_codec_name - store the codec name
+ * get_codec_name - store the codec name
  */
-void snd_hda_get_codec_name(struct hda_codec *codec,
-                           char *name, int namelen)
+static int get_codec_name(struct hda_codec *codec)
 {
        const struct hda_vendor_id *c;
        const char *vendor = NULL;
        u16 vendor_id = codec->vendor_id >> 16;
-       char tmp[16];
+       char tmp[16], name[32];
 
        for (c = hda_vendor_ids; c->id; c++) {
                if (c->id == vendor_id) {
@@ -503,16 +614,21 @@ void snd_hda_get_codec_name(struct hda_codec *codec,
                vendor = tmp;
        }
        if (codec->preset && codec->preset->name)
-               snprintf(name, namelen, "%s %s", vendor, codec->preset->name);
+               snprintf(name, sizeof(name), "%s %s", vendor,
+                        codec->preset->name);
        else
-               snprintf(name, namelen, "%s ID %x", vendor,
+               snprintf(name, sizeof(name), "%s ID %x", vendor,
                         codec->vendor_id & 0xffff);
+       codec->name = kstrdup(name, GFP_KERNEL);
+       if (!codec->name)
+               return -ENOMEM;
+       return 0;
 }
 
 /*
  * look for an AFG and MFG nodes
  */
-static void __devinit setup_fg_nodes(struct hda_codec *codec)
+static void /*__devinit*/ setup_fg_nodes(struct hda_codec *codec)
 {
        int i, total_nodes;
        hda_nid_t nid;
@@ -571,11 +687,15 @@ static void snd_hda_codec_free(struct hda_codec *codec)
        flush_scheduled_work();
 #endif
        list_del(&codec->list);
+       snd_array_free(&codec->mixers);
        codec->bus->caddr_tbl[codec->addr] = NULL;
        if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
+       module_put(codec->owner);
        free_hda_cache(&codec->amp_cache);
        free_hda_cache(&codec->cmd_cache);
+       kfree(codec->name);
+       kfree(codec->modelname);
        kfree(codec->wcaps);
        kfree(codec);
 }
@@ -588,8 +708,8 @@ static void snd_hda_codec_free(struct hda_codec *codec)
  *
  * Returns 0 if successful, or a negative error code.
  */
-int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
-                               struct hda_codec **codecp)
+int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
+                                   int do_init, struct hda_codec **codecp)
 {
        struct hda_codec *codec;
        char component[31];
@@ -617,6 +737,14 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
        mutex_init(&codec->spdif_mutex);
        init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
        init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
+       snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32);
+       if (codec->bus->modelname) {
+               codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
+               if (!codec->modelname) {
+                       snd_hda_codec_free(codec);
+                       return -ENODEV;
+               }
+       }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
@@ -662,12 +790,44 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
                        snd_hda_codec_read(codec, nid, 0,
                                           AC_VERB_GET_SUBSYSTEM_ID, 0);
        }
+       if (bus->modelname)
+               codec->modelname = kstrdup(bus->modelname, GFP_KERNEL);
+
+       if (do_init) {
+               err = snd_hda_codec_configure(codec);
+               if (err < 0) {
+                       snd_hda_codec_free(codec);
+                       return err;
+               }
+       }
+       snd_hda_codec_proc_new(codec);
+
+       snd_hda_create_hwdep(codec);
+
+       sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id,
+               codec->subsystem_id, codec->revision_id);
+       snd_component_add(codec->bus->card, component);
+
+       if (codecp)
+               *codecp = codec;
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_new);
+
+int snd_hda_codec_configure(struct hda_codec *codec)
+{
+       int err;
 
        codec->preset = find_codec_preset(codec);
+       if (!codec->name) {
+               err = get_codec_name(codec);
+               if (err < 0)
+                       return err;
+       }
        /* audio codec should override the mixer name */
-       if (codec->afg || !*bus->card->mixername)
-               snd_hda_get_codec_name(codec, bus->card->mixername,
-                                      sizeof(bus->card->mixername));
+       if (codec->afg || !*codec->bus->card->mixername)
+               strlcpy(codec->bus->card->mixername, codec->name,
+                       sizeof(codec->bus->card->mixername));
 
        if (is_generic_config(codec)) {
                err = snd_hda_parse_generic_codec(codec);
@@ -684,25 +844,9 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
                printk(KERN_ERR "hda-codec: No codec parser is available\n");
 
  patched:
-       if (err < 0) {
-               snd_hda_codec_free(codec);
-               return err;
-       }
-
-       if (codec->patch_ops.unsol_event)
-               init_unsol_queue(bus);
-
-       snd_hda_codec_proc_new(codec);
-#ifdef CONFIG_SND_HDA_HWDEP
-       snd_hda_create_hwdep(codec);
-#endif
-
-       sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, codec->subsystem_id, codec->revision_id);
-       snd_component_add(codec->bus->card, component);
-
-       if (codecp)
-               *codecp = codec;
-       return 0;
+       if (!err && codec->patch_ops.unsol_event)
+               err = init_unsol_queue(codec->bus);
+       return err;
 }
 
 /**
@@ -728,6 +872,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
        msleep(1);
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format);
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
 
 void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
 {
@@ -741,6 +886,7 @@ void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
 #endif
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
 
 /*
  * amp access functions
@@ -752,17 +898,17 @@ void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
 #define INFO_AMP_VOL(ch)       (1 << (1 + (ch)))
 
 /* initialize the hash table */
-static void __devinit init_hda_cache(struct hda_cache_rec *cache,
+static void /*__devinit*/ init_hda_cache(struct hda_cache_rec *cache,
                                     unsigned int record_size)
 {
        memset(cache, 0, sizeof(*cache));
        memset(cache->hash, 0xff, sizeof(cache->hash));
-       cache->record_size = record_size;
+       snd_array_init(&cache->buf, record_size, 64);
 }
 
 static void free_hda_cache(struct hda_cache_rec *cache)
 {
-       kfree(cache->buffer);
+       snd_array_free(&cache->buf);
 }
 
 /* query the hash.  allocate an entry if not found. */
@@ -774,35 +920,17 @@ static struct hda_cache_head  *get_alloc_hash(struct hda_cache_rec *cache,
        struct hda_cache_head *info;
 
        while (cur != 0xffff) {
-               info = (struct hda_cache_head *)(cache->buffer +
-                                                cur * cache->record_size);
+               info = snd_array_elem(&cache->buf, cur);
                if (info->key == key)
                        return info;
                cur = info->next;
        }
 
        /* add a new hash entry */
-       if (cache->num_entries >= cache->size) {
-               /* reallocate the array */
-               unsigned int new_size = cache->size + 64;
-               void *new_buffer;
-               new_buffer = kcalloc(new_size, cache->record_size, GFP_KERNEL);
-               if (!new_buffer) {
-                       snd_printk(KERN_ERR "hda_codec: "
-                                  "can't malloc amp_info\n");
-                       return NULL;
-               }
-               if (cache->buffer) {
-                       memcpy(new_buffer, cache->buffer,
-                              cache->size * cache->record_size);
-                       kfree(cache->buffer);
-               }
-               cache->size = new_size;
-               cache->buffer = new_buffer;
-       }
-       cur = cache->num_entries++;
-       info = (struct hda_cache_head *)(cache->buffer +
-                                        cur * cache->record_size);
+       info = snd_array_new(&cache->buf);
+       if (!info)
+               return NULL;
+       cur = snd_array_index(&cache->buf, info);
        info->key = key;
        info->val = 0;
        info->next = cache->hash[idx];
@@ -840,6 +968,7 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
        }
        return info->amp_caps;
 }
+EXPORT_SYMBOL_HDA(query_amp_caps);
 
 int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
                              unsigned int caps)
@@ -853,6 +982,7 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
        info->head.val |= INFO_AMP_CAPS;
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
 
 /*
  * read the current volume to info
@@ -906,6 +1036,7 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
                return 0;
        return get_vol_mute(codec, info, nid, ch, direction, index);
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
 
 /*
  * update the AMP value, mask = bit mask to set, val = the value
@@ -925,6 +1056,7 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
        put_vol_mute(codec, info, nid, ch, direction, idx, val);
        return 1;
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
 
 /*
  * update the AMP stereo with the same mask and value
@@ -938,15 +1070,16 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
                                                idx, mask, val);
        return ret;
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
 
 #ifdef SND_HDA_NEEDS_RESUME
 /* resume the all amp commands from the cache */
 void snd_hda_codec_resume_amp(struct hda_codec *codec)
 {
-       struct hda_amp_info *buffer = codec->amp_cache.buffer;
+       struct hda_amp_info *buffer = codec->amp_cache.buf.list;
        int i;
 
-       for (i = 0; i < codec->amp_cache.size; i++, buffer++) {
+       for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) {
                u32 key = buffer->head.key;
                hda_nid_t nid;
                unsigned int idx, dir, ch;
@@ -963,6 +1096,7 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
                }
        }
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
 #endif /* SND_HDA_NEEDS_RESUME */
 
 /* volume */
@@ -990,6 +1124,7 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
        uinfo->value.integer.max = caps;
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info);
 
 int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
@@ -1009,6 +1144,7 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
                        & HDA_AMP_VOLMASK;
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
 
 int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
@@ -1033,6 +1169,7 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
        snd_hda_power_down(codec);
        return change;
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put);
 
 int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                          unsigned int size, unsigned int __user *_tlv)
@@ -1059,6 +1196,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                return -EFAULT;
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv);
 
 /*
  * set (static) TLV for virtual master volume; recalculated as max 0dB
@@ -1078,6 +1216,7 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
        tlv[2] = -nums * step;
        tlv[3] = step;
 }
+EXPORT_SYMBOL_HDA(snd_hda_set_vmaster_tlv);
 
 /* find a mixer control element with the given name */
 static struct snd_kcontrol *
@@ -1097,6 +1236,69 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
 {
        return _snd_hda_find_mixer_ctl(codec, name, 0);
 }
+EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
+
+/* Add a control element and assign to the codec */
+int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl)
+{
+       int err;
+       struct snd_kcontrol **knewp;
+
+       err = snd_ctl_add(codec->bus->card, kctl);
+       if (err < 0)
+               return err;
+       knewp = snd_array_new(&codec->mixers);
+       if (!knewp)
+               return -ENOMEM;
+       *knewp = kctl;
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+/* Clear all controls assigned to the given codec */
+void snd_hda_ctls_clear(struct hda_codec *codec)
+{
+       int i;
+       struct snd_kcontrol **kctls = codec->mixers.list;
+       for (i = 0; i < codec->mixers.used; i++)
+               snd_ctl_remove(codec->bus->card, kctls[i]);
+       snd_array_free(&codec->mixers);
+}
+
+void snd_hda_codec_reset(struct hda_codec *codec)
+{
+       int i;
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       cancel_delayed_work(&codec->power_work);
+       flush_scheduled_work();
+#endif
+       snd_hda_ctls_clear(codec);
+       /* relase PCMs */
+       for (i = 0; i < codec->num_pcms; i++) {
+               if (codec->pcm_info[i].pcm) {
+                       snd_device_free(codec->bus->card,
+                                       codec->pcm_info[i].pcm);
+                       clear_bit(codec->pcm_info[i].device,
+                                 codec->bus->pcm_dev_bits);
+               }
+       }
+       if (codec->patch_ops.free)
+               codec->patch_ops.free(codec);
+       codec->proc_widget_hook = NULL;
+       codec->spec = NULL;
+       free_hda_cache(&codec->amp_cache);
+       free_hda_cache(&codec->cmd_cache);
+       init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
+       init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
+       codec->num_pcms = 0;
+       codec->pcm_info = NULL;
+       codec->preset = NULL;
+       module_put(codec->owner);
+       codec->owner = NULL;
+}
+#endif /* CONFIG_SND_HDA_RECONFIG */
 
 /* create a virtual master control and add slaves */
 int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
@@ -1115,7 +1317,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
        kctl = snd_ctl_make_virtual_master(name, tlv);
        if (!kctl)
                return -ENOMEM;
-       err = snd_ctl_add(codec->bus->card, kctl);
+       err = snd_hda_ctl_add(codec, kctl);
        if (err < 0)
                return err;
        
@@ -1133,6 +1335,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
        }
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_add_vmaster);
 
 /* switch */
 int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
@@ -1146,6 +1349,7 @@ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
        uinfo->value.integer.max = 1;
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info);
 
 int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
@@ -1165,6 +1369,7 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
                         HDA_AMP_MUTE) ? 0 : 1;
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get);
 
 int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
@@ -1195,6 +1400,7 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
        snd_hda_power_down(codec);
        return change;
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
 
 /*
  * bound volume controls
@@ -1220,6 +1426,7 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
        mutex_unlock(&codec->spdif_mutex);
        return err;
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get);
 
 int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
@@ -1243,6 +1450,7 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
        mutex_unlock(&codec->spdif_mutex);
        return err < 0 ? err : change;
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put);
 
 /*
  * generic bound volume/swtich controls
@@ -1262,6 +1470,7 @@ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
        mutex_unlock(&codec->spdif_mutex);
        return err;
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info);
 
 int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
@@ -1278,6 +1487,7 @@ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
        mutex_unlock(&codec->spdif_mutex);
        return err;
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get);
 
 int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
@@ -1300,6 +1510,7 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
        mutex_unlock(&codec->spdif_mutex);
        return err < 0 ? err : change;
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put);
 
 int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                           unsigned int size, unsigned int __user *tlv)
@@ -1316,6 +1527,7 @@ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
        mutex_unlock(&codec->spdif_mutex);
        return err;
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_tlv);
 
 struct hda_ctl_ops snd_hda_bind_vol = {
        .info = snd_hda_mixer_amp_volume_info,
@@ -1323,6 +1535,7 @@ struct hda_ctl_ops snd_hda_bind_vol = {
        .put = snd_hda_mixer_amp_volume_put,
        .tlv = snd_hda_mixer_amp_tlv
 };
+EXPORT_SYMBOL_HDA(snd_hda_bind_vol);
 
 struct hda_ctl_ops snd_hda_bind_sw = {
        .info = snd_hda_mixer_amp_switch_info,
@@ -1330,6 +1543,7 @@ struct hda_ctl_ops snd_hda_bind_sw = {
        .put = snd_hda_mixer_amp_switch_put,
        .tlv = snd_hda_mixer_amp_tlv
 };
+EXPORT_SYMBOL_HDA(snd_hda_bind_sw);
 
 /*
  * SPDIF out controls
@@ -1577,9 +1791,11 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
        }
        for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
                kctl = snd_ctl_new1(dig_mix, codec);
+               if (!kctl)
+                       return -ENOMEM;
                kctl->id.index = idx;
                kctl->private_value = nid;
-               err = snd_ctl_add(codec->bus->card, kctl);
+               err = snd_hda_ctl_add(codec, kctl);
                if (err < 0)
                        return err;
        }
@@ -1589,6 +1805,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
        codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls);
 
 /*
  * SPDIF sharing with analog output
@@ -1623,9 +1840,10 @@ int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
        if (!mout->dig_out_nid)
                return 0;
        /* ATTENTION: here mout is passed as private_data, instead of codec */
-       return snd_ctl_add(codec->bus->card,
+       return snd_hda_ctl_add(codec,
                           snd_ctl_new1(&spdif_share_sw, mout));
 }
+EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw);
 
 /*
  * SPDIF input
@@ -1725,7 +1943,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
        for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
                kctl = snd_ctl_new1(dig_mix, codec);
                kctl->private_value = nid;
-               err = snd_ctl_add(codec->bus->card, kctl);
+               err = snd_hda_ctl_add(codec, kctl);
                if (err < 0)
                        return err;
        }
@@ -1735,6 +1953,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
                AC_DIG1_ENABLE;
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
 
 #ifdef SND_HDA_NEEDS_RESUME
 /*
@@ -1761,10 +1980,14 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
 int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
                              int direct, unsigned int verb, unsigned int parm)
 {
+       struct hda_bus *bus = codec->bus;
+       unsigned int res;
        int err;
+
+       res = make_codec_cmd(codec, nid, direct, verb, parm);
        snd_hda_power_up(codec);
-       mutex_lock(&codec->bus->cmd_mutex);
-       err = codec->bus->ops.command(codec, nid, direct, verb, parm);
+       mutex_lock(&bus->cmd_mutex);
+       err = bus->ops.command(bus, res);
        if (!err) {
                struct hda_cache_head *c;
                u32 key = build_cmd_cache_key(nid, verb);
@@ -1772,18 +1995,19 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
                if (c)
                        c->val = parm;
        }
-       mutex_unlock(&codec->bus->cmd_mutex);
+       mutex_unlock(&bus->cmd_mutex);
        snd_hda_power_down(codec);
        return err;
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
 
 /* resume the all commands from the cache */
 void snd_hda_codec_resume_cache(struct hda_codec *codec)
 {
-       struct hda_cache_head *buffer = codec->cmd_cache.buffer;
+       struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
        int i;
 
-       for (i = 0; i < codec->cmd_cache.size; i++, buffer++) {
+       for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) {
                u32 key = buffer->key;
                if (!key)
                        continue;
@@ -1791,6 +2015,7 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec)
                                    get_cmd_cache_cmd(key), buffer->val);
        }
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache);
 
 /**
  * snd_hda_sequence_write_cache - sequence writes with caching
@@ -1808,6 +2033,7 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
                snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb,
                                          seq->param);
 }
+EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
 #endif /* SND_HDA_NEEDS_RESUME */
 
 /*
@@ -1868,6 +2094,17 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
        }
 }
 
+#ifdef CONFIG_SND_HDA_HWDEP
+/* execute additional init verbs */
+static void hda_exec_init_verbs(struct hda_codec *codec)
+{
+       if (codec->init_verbs.list)
+               snd_hda_sequence_write(codec, codec->init_verbs.list);
+}
+#else
+static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
+#endif
+
 #ifdef SND_HDA_NEEDS_RESUME
 /*
  * call suspend and power-down; used both from PM and power-save
@@ -1894,6 +2131,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
        hda_set_power_state(codec,
                            codec->afg ? codec->afg : codec->mfg,
                            AC_PWRST_D0);
+       hda_exec_init_verbs(codec);
        if (codec->patch_ops.resume)
                codec->patch_ops.resume(codec);
        else {
@@ -1914,28 +2152,37 @@ static void hda_call_codec_resume(struct hda_codec *codec)
  *
  * Returns 0 if successful, otherwise a negative error code.
  */
-int __devinit snd_hda_build_controls(struct hda_bus *bus)
+int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus)
 {
        struct hda_codec *codec;
 
        list_for_each_entry(codec, &bus->codec_list, list) {
-               int err = 0;
-               /* fake as if already powered-on */
-               hda_keep_power_on(codec);
-               /* then fire up */
-               hda_set_power_state(codec,
-                                   codec->afg ? codec->afg : codec->mfg,
-                                   AC_PWRST_D0);
-               /* continue to initialize... */
-               if (codec->patch_ops.init)
-                       err = codec->patch_ops.init(codec);
-               if (!err && codec->patch_ops.build_controls)
-                       err = codec->patch_ops.build_controls(codec);
-               snd_hda_power_down(codec);
+               int err = snd_hda_codec_build_controls(codec);
                if (err < 0)
                        return err;
        }
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_build_controls);
 
+int snd_hda_codec_build_controls(struct hda_codec *codec)
+{
+       int err = 0;
+       /* fake as if already powered-on */
+       hda_keep_power_on(codec);
+       /* then fire up */
+       hda_set_power_state(codec,
+                           codec->afg ? codec->afg : codec->mfg,
+                           AC_PWRST_D0);
+       hda_exec_init_verbs(codec);
+       /* continue to initialize... */
+       if (codec->patch_ops.init)
+               err = codec->patch_ops.init(codec);
+       if (!err && codec->patch_ops.build_controls)
+               err = codec->patch_ops.build_controls(codec);
+       snd_hda_power_down(codec);
+       if (err < 0)
+               return err;
        return 0;
 }
 
@@ -2028,6 +2275,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
 
        return val;
 }
+EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
 
 /**
  * snd_hda_query_supported_pcm - query the supported PCM rates and formats
@@ -2042,7 +2290,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
  *
  * Returns 0 if successful, otherwise a negative error code.
  */
-int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
+static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
                                u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
 {
        int i;
@@ -2207,6 +2455,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
 
        return 1;
 }
+EXPORT_SYMBOL_HDA(snd_hda_is_supported_format);
 
 /*
  * PCM stuff
@@ -2236,8 +2485,8 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
        return 0;
 }
 
-static int __devinit set_pcm_default_values(struct hda_codec *codec,
-                                           struct hda_pcm_stream *info)
+static int set_pcm_default_values(struct hda_codec *codec,
+                                 struct hda_pcm_stream *info)
 {
        /* query support PCM information from the given NID */
        if (info->nid && (!info->rates || !info->formats)) {
@@ -2263,6 +2512,110 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec,
        return 0;
 }
 
+/*
+ * get the empty PCM device number to assign
+ */
+static int get_empty_pcm_device(struct hda_bus *bus, int type)
+{
+       static const char *dev_name[HDA_PCM_NTYPES] = {
+               "Audio", "SPDIF", "HDMI", "Modem"
+       };
+       /* starting device index for each PCM type */
+       static int dev_idx[HDA_PCM_NTYPES] = {
+               [HDA_PCM_TYPE_AUDIO] = 0,
+               [HDA_PCM_TYPE_SPDIF] = 1,
+               [HDA_PCM_TYPE_HDMI] = 3,
+               [HDA_PCM_TYPE_MODEM] = 6
+       };
+       /* normal audio device indices; not linear to keep compatibility */
+       static int audio_idx[4] = { 0, 2, 4, 5 };
+       int i, dev;
+
+       switch (type) {
+       case HDA_PCM_TYPE_AUDIO:
+               for (i = 0; i < ARRAY_SIZE(audio_idx); i++) {
+                       dev = audio_idx[i];
+                       if (!test_bit(dev, bus->pcm_dev_bits))
+                               break;
+               }
+               if (i >= ARRAY_SIZE(audio_idx)) {
+                       snd_printk(KERN_WARNING "Too many audio devices\n");
+                       return -EAGAIN;
+               }
+               break;
+       case HDA_PCM_TYPE_SPDIF:
+       case HDA_PCM_TYPE_HDMI:
+       case HDA_PCM_TYPE_MODEM:
+               dev = dev_idx[type];
+               if (test_bit(dev, bus->pcm_dev_bits)) {
+                       snd_printk(KERN_WARNING "%s already defined\n",
+                                  dev_name[type]);
+                       return -EAGAIN;
+               }
+               break;
+       default:
+               snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
+               return -EINVAL;
+       }
+       set_bit(dev, bus->pcm_dev_bits);
+       return dev;
+}
+
+/*
+ * attach a new PCM stream
+ */
+static int snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm)
+{
+       struct hda_bus *bus = codec->bus;
+       struct hda_pcm_stream *info;
+       int stream, err;
+
+       if (snd_BUG_ON(!pcm->name))
+               return -EINVAL;
+       for (stream = 0; stream < 2; stream++) {
+               info = &pcm->stream[stream];
+               if (info->substreams) {
+                       err = set_pcm_default_values(codec, info);
+                       if (err < 0)
+                               return err;
+               }
+       }
+       return bus->ops.attach_pcm(bus, codec, pcm);
+}
+
+/* assign all PCMs of the given codec */
+int snd_hda_codec_build_pcms(struct hda_codec *codec)
+{
+       unsigned int pcm;
+       int err;
+
+       if (!codec->num_pcms) {
+               if (!codec->patch_ops.build_pcms)
+                       return 0;
+               err = codec->patch_ops.build_pcms(codec);
+               if (err < 0)
+                       return err;
+       }
+       for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+               struct hda_pcm *cpcm = &codec->pcm_info[pcm];
+               int dev;
+
+               if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
+                       return 0; /* no substreams assigned */
+
+               if (!cpcm->pcm) {
+                       dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type);
+                       if (dev < 0)
+                               return 0;
+                       cpcm->device = dev;
+                       err = snd_hda_attach_pcm(codec, cpcm);
+                       if (err < 0)
+                               return err;
+               }
+       }
+       return 0;
+}
+
 /**
  * snd_hda_build_pcms - build PCM information
  * @bus: the BUS
@@ -2294,27 +2647,13 @@ int __devinit snd_hda_build_pcms(struct hda_bus *bus)
        struct hda_codec *codec;
 
        list_for_each_entry(codec, &bus->codec_list, list) {
-               unsigned int pcm, s;
-               int err;
-               if (!codec->patch_ops.build_pcms)
-                       continue;
-               err = codec->patch_ops.build_pcms(codec);
+               int err = snd_hda_codec_build_pcms(codec);
                if (err < 0)
                        return err;
-               for (pcm = 0; pcm < codec->num_pcms; pcm++) {
-                       for (s = 0; s < 2; s++) {
-                               struct hda_pcm_stream *info;
-                               info = &codec->pcm_info[pcm].stream[s];
-                               if (!info->substreams)
-                                       continue;
-                               err = set_pcm_default_values(codec, info);
-                               if (err < 0)
-                                       return err;
-                       }
-               }
        }
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_build_pcms);
 
 /**
  * snd_hda_check_board_config - compare the current codec with the config table
@@ -2333,11 +2672,11 @@ int snd_hda_check_board_config(struct hda_codec *codec,
                               int num_configs, const char **models,
                               const struct snd_pci_quirk *tbl)
 {
-       if (codec->bus->modelname && models) {
+       if (codec->modelname && models) {
                int i;
                for (i = 0; i < num_configs; i++) {
                        if (models[i] &&
-                           !strcmp(codec->bus->modelname, models[i])) {
+                           !strcmp(codec->modelname, models[i])) {
                                snd_printd(KERN_INFO "hda_codec: model '%s' is "
                                           "selected\n", models[i]);
                                return i;
@@ -2370,6 +2709,7 @@ int snd_hda_check_board_config(struct hda_codec *codec,
        }
        return -1;
 }
+EXPORT_SYMBOL_HDA(snd_hda_check_board_config);
 
 /**
  * snd_hda_add_new_ctls - create controls from the array
@@ -2390,7 +2730,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
                kctl = snd_ctl_new1(knew, codec);
                if (!kctl)
                        return -ENOMEM;
-               err = snd_ctl_add(codec->bus->card, kctl);
+               err = snd_hda_ctl_add(codec, kctl);
                if (err < 0) {
                        if (!codec->addr)
                                return err;
@@ -2398,13 +2738,14 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
                        if (!kctl)
                                return -ENOMEM;
                        kctl->id.device = codec->addr;
-                       err = snd_ctl_add(codec->bus->card, kctl);
+                       err = snd_hda_ctl_add(codec, kctl);
                        if (err < 0)
                                return err;
                }
        }
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
@@ -2414,6 +2755,7 @@ static void hda_power_work(struct work_struct *work)
 {
        struct hda_codec *codec =
                container_of(work, struct hda_codec, power_work.work);
+       struct hda_bus *bus = codec->bus;
 
        if (!codec->power_on || codec->power_count) {
                codec->power_transition = 0;
@@ -2421,8 +2763,8 @@ static void hda_power_work(struct work_struct *work)
        }
 
        hda_call_codec_suspend(codec);
-       if (codec->bus->ops.pm_notify)
-               codec->bus->ops.pm_notify(codec);
+       if (bus->ops.pm_notify)
+               bus->ops.pm_notify(bus);
 }
 
 static void hda_keep_power_on(struct hda_codec *codec)
@@ -2433,29 +2775,39 @@ static void hda_keep_power_on(struct hda_codec *codec)
 
 void snd_hda_power_up(struct hda_codec *codec)
 {
+       struct hda_bus *bus = codec->bus;
+
        codec->power_count++;
        if (codec->power_on || codec->power_transition)
                return;
 
        codec->power_on = 1;
-       if (codec->bus->ops.pm_notify)
-               codec->bus->ops.pm_notify(codec);
+       if (bus->ops.pm_notify)
+               bus->ops.pm_notify(bus);
        hda_call_codec_resume(codec);
        cancel_delayed_work(&codec->power_work);
        codec->power_transition = 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_power_up);
+
+#define power_save(codec)      \
+       ((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
+
+#define power_save(codec)      \
+       ((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
 
 void snd_hda_power_down(struct hda_codec *codec)
 {
        --codec->power_count;
        if (!codec->power_on || codec->power_count || codec->power_transition)
                return;
-       if (power_save) {
+       if (power_save(codec)) {
                codec->power_transition = 1; /* avoid reentrance */
                schedule_delayed_work(&codec->power_work,
-                                     msecs_to_jiffies(power_save * 1000));
+                               msecs_to_jiffies(power_save(codec) * 1000));
        }
 }
+EXPORT_SYMBOL_HDA(snd_hda_power_down);
 
 int snd_hda_check_amp_list_power(struct hda_codec *codec,
                                 struct hda_loopback_check *check,
@@ -2492,6 +2844,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
        }
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power);
 #endif
 
 /*
@@ -2511,6 +2864,7 @@ int snd_hda_ch_mode_info(struct hda_codec *codec,
                chmode[uinfo->value.enumerated.item].channels);
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info);
 
 int snd_hda_ch_mode_get(struct hda_codec *codec,
                        struct snd_ctl_elem_value *ucontrol,
@@ -2528,6 +2882,7 @@ int snd_hda_ch_mode_get(struct hda_codec *codec,
        }
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get);
 
 int snd_hda_ch_mode_put(struct hda_codec *codec,
                        struct snd_ctl_elem_value *ucontrol,
@@ -2548,6 +2903,7 @@ int snd_hda_ch_mode_put(struct hda_codec *codec,
                snd_hda_sequence_write_cache(codec, chmode[mode].sequence);
        return 1;
 }
+EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put);
 
 /*
  * input MUX helper
@@ -2568,6 +2924,7 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux,
        strcpy(uinfo->value.enumerated.name, imux->items[index].label);
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_input_mux_info);
 
 int snd_hda_input_mux_put(struct hda_codec *codec,
                          const struct hda_input_mux *imux,
@@ -2589,6 +2946,7 @@ int snd_hda_input_mux_put(struct hda_codec *codec,
        *cur_val = idx;
        return 1;
 }
+EXPORT_SYMBOL_HDA(snd_hda_input_mux_put);
 
 
 /*
@@ -2641,6 +2999,7 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec,
        mutex_unlock(&codec->spdif_mutex);
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open);
 
 int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
                                  struct hda_multi_out *mout,
@@ -2653,6 +3012,7 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
        mutex_unlock(&codec->spdif_mutex);
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare);
 
 /*
  * release the digital out
@@ -2665,6 +3025,7 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec,
        mutex_unlock(&codec->spdif_mutex);
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close);
 
 /*
  * set up more restrictions for analog out
@@ -2704,6 +3065,7 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
        return snd_pcm_hw_constraint_step(substream->runtime, 0,
                                          SNDRV_PCM_HW_PARAM_CHANNELS, 2);
 }
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open);
 
 /*
  * set up the i/o for analog out
@@ -2762,6 +3124,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
        }
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);
 
 /*
  * clean up the setting for analog out
@@ -2788,6 +3151,7 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
        mutex_unlock(&codec->spdif_mutex);
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup);
 
 /*
  * Helper for automatic pin configuration
@@ -3073,11 +3437,13 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config);
 
 /* labels for input pins */
 const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = {
        "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
 };
+EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
 
 
 #ifdef CONFIG_PM
@@ -3105,11 +3471,11 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
        }
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_suspend);
 
 /**
  * snd_hda_resume - resume the codecs
  * @bus: the HDA bus
- * @state: resume state
  *
  * Returns 0 if successful.
  *
@@ -3126,16 +3492,79 @@ int snd_hda_resume(struct hda_bus *bus)
        }
        return 0;
 }
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-int snd_hda_codecs_inuse(struct hda_bus *bus)
-{
-       struct hda_codec *codec;
+EXPORT_SYMBOL_HDA(snd_hda_resume);
+#endif /* CONFIG_PM */
 
-       list_for_each_entry(codec, &bus->codec_list, list) {
-               if (snd_hda_codec_needs_resume(codec))
-                       return 1;
+/*
+ * generic arrays
+ */
+
+/* get a new element from the given array
+ * if it exceeds the pre-allocated array size, re-allocate the array
+ */
+void *snd_array_new(struct snd_array *array)
+{
+       if (array->used >= array->alloced) {
+               int num = array->alloced + array->alloc_align;
+               void *nlist;
+               if (snd_BUG_ON(num >= 4096))
+                       return NULL;
+               nlist = kcalloc(num + 1, array->elem_size, GFP_KERNEL);
+               if (!nlist)
+                       return NULL;
+               if (array->list) {
+                       memcpy(nlist, array->list,
+                              array->elem_size * array->alloced);
+                       kfree(array->list);
+               }
+               array->list = nlist;
+               array->alloced = num;
        }
-       return 0;
+       return snd_array_elem(array, array->used++);
 }
-#endif
-#endif
+EXPORT_SYMBOL_HDA(snd_array_new);
+
+/* free the given array elements */
+void snd_array_free(struct snd_array *array)
+{
+       kfree(array->list);
+       array->used = 0;
+       array->alloced = 0;
+       array->list = NULL;
+}
+EXPORT_SYMBOL_HDA(snd_array_free);
+
+/*
+ * used by hda_proc.c and hda_eld.c
+ */
+void snd_print_pcm_rates(int pcm, char *buf, int buflen)
+{
+       static unsigned int rates[] = {
+               8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
+               96000, 176400, 192000, 384000
+       };
+       int i, j;
+
+       for (i = 0, j = 0; i < ARRAY_SIZE(rates); i++)
+               if (pcm & (1 << i))
+                       j += snprintf(buf + j, buflen - j,  " %d", rates[i]);
+
+       buf[j] = '\0'; /* necessary when j == 0 */
+}
+EXPORT_SYMBOL_HDA(snd_print_pcm_rates);
+
+void snd_print_pcm_bits(int pcm, char *buf, int buflen)
+{
+       static unsigned int bits[] = { 8, 16, 20, 24, 32 };
+       int i, j;
+
+       for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++)
+               if (pcm & (AC_SUPPCM_BITS_8 << i))
+                       j += snprintf(buf + j, buflen - j,  " %d", bits[i]);
+
+       buf[j] = '\0'; /* necessary when j == 0 */
+}
+EXPORT_SYMBOL_HDA(snd_print_pcm_bits);
+
+MODULE_DESCRIPTION("HDA codec core");
+MODULE_LICENSE("GPL");
index 60468f5..729fc76 100644 (file)
@@ -519,6 +519,36 @@ enum {
 /* max. codec address */
 #define HDA_MAX_CODEC_ADDRESS  0x0f
 
+/*
+ * generic arrays
+ */
+struct snd_array {
+       unsigned int used;
+       unsigned int alloced;
+       unsigned int elem_size;
+       unsigned int alloc_align;
+       void *list;
+};
+
+void *snd_array_new(struct snd_array *array);
+void snd_array_free(struct snd_array *array);
+static inline void snd_array_init(struct snd_array *array, unsigned int size,
+                                 unsigned int align)
+{
+       array->elem_size = size;
+       array->alloc_align = align;
+}
+
+static inline void *snd_array_elem(struct snd_array *array, unsigned int idx)
+{
+       return array->list + idx * array->elem_size;
+}
+
+static inline unsigned int snd_array_index(struct snd_array *array, void *ptr)
+{
+       return (unsigned long)(ptr - array->list) / array->elem_size;
+}
+
 /*
  * Structures
  */
@@ -536,15 +566,17 @@ typedef u16 hda_nid_t;
 /* bus operators */
 struct hda_bus_ops {
        /* send a single command */
-       int (*command)(struct hda_codec *codec, hda_nid_t nid, int direct,
-                      unsigned int verb, unsigned int parm);
+       int (*command)(struct hda_bus *bus, unsigned int cmd);
        /* get a response from the last command */
-       unsigned int (*get_response)(struct hda_codec *codec);
+       unsigned int (*get_response)(struct hda_bus *bus);
        /* free the private data */
        void (*private_free)(struct hda_bus *);
+       /* attach a PCM stream */
+       int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec,
+                         struct hda_pcm *pcm);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        /* notify power-up/down from codec to controller */
-       void (*pm_notify)(struct hda_codec *codec);
+       void (*pm_notify)(struct hda_bus *bus);
 #endif
 };
 
@@ -553,6 +585,7 @@ struct hda_bus_template {
        void *private_data;
        struct pci_dev *pci;
        const char *modelname;
+       int *power_save;
        struct hda_bus_ops ops;
 };
 
@@ -569,6 +602,7 @@ struct hda_bus {
        void *private_data;
        struct pci_dev *pci;
        const char *modelname;
+       int *power_save;
        struct hda_bus_ops ops;
 
        /* codec linked list */
@@ -581,10 +615,12 @@ struct hda_bus {
        /* unsolicited event queue */
        struct hda_bus_unsolicited *unsol;
 
-       struct snd_info_entry *proc;
+       /* assigned PCMs */
+       DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES);
 
        /* misc op flags */
        unsigned int needs_damn_long_delay :1;
+       unsigned int shutdown :1;       /* being unloaded */
 };
 
 /*
@@ -604,6 +640,16 @@ struct hda_codec_preset {
        int (*patch)(struct hda_codec *codec);
 };
        
+struct hda_codec_preset_list {
+       const struct hda_codec_preset *preset;
+       struct module *owner;
+       struct list_head list;
+};
+
+/* initial hook */
+int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset);
+int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset);
+
 /* ops set by the preset patch */
 struct hda_codec_ops {
        int (*build_controls)(struct hda_codec *codec);
@@ -635,10 +681,7 @@ struct hda_amp_info {
 
 struct hda_cache_rec {
        u16 hash[64];                   /* hash table for index */
-       unsigned int num_entries;       /* number of assigned entries */
-       unsigned int size;              /* allocated size */
-       unsigned int record_size;       /* record size (including header) */
-       void *buffer;                   /* hash table entries */
+       struct snd_array buf;           /* record entries */
 };
 
 /* PCM callbacks */
@@ -680,7 +723,8 @@ struct hda_pcm {
        char *name;
        struct hda_pcm_stream stream[2];
        unsigned int pcm_type;  /* HDA_PCM_TYPE_XXX */
-       int device;     /* assigned device number */
+       int device;             /* device number to assign */
+       struct snd_pcm *pcm;    /* assigned PCM instance */
 };
 
 /* codec information */
@@ -699,6 +743,9 @@ struct hda_codec {
 
        /* detected preset */
        const struct hda_codec_preset *preset;
+       struct module *owner;
+       const char *name;       /* codec name */
+       const char *modelname;  /* model name for preset */
 
        /* set by patch */
        struct hda_codec_ops patch_ops;
@@ -718,6 +765,8 @@ struct hda_codec {
        hda_nid_t start_nid;
        u32 *wcaps;
 
+       struct snd_array mixers;        /* list of assigned mixer elements */
+
        struct hda_cache_rec amp_cache; /* cache for amp access */
        struct hda_cache_rec cmd_cache; /* cache for other commands */
 
@@ -727,7 +776,11 @@ struct hda_codec {
        unsigned int spdif_in_enable;   /* SPDIF input enable? */
        hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
 
+#ifdef CONFIG_SND_HDA_HWDEP
        struct snd_hwdep *hwdep;        /* assigned hwdep device */
+       struct snd_array init_verbs;    /* additional init verbs */
+       struct snd_array hints;         /* additional hints */
+#endif
 
        /* misc flags */
        unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each
@@ -740,6 +793,10 @@ struct hda_codec {
        int power_count;        /* current (global) power refcount */
        struct delayed_work power_work; /* delayed task for powerdown */
 #endif
+
+       /* codec-specific additional proc output */
+       void (*proc_widget_hook)(struct snd_info_buffer *buffer,
+                                struct hda_codec *codec, hda_nid_t nid);
 };
 
 /* direction */
@@ -754,7 +811,7 @@ enum {
 int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
                    struct hda_bus **busp);
 int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
-                     struct hda_codec **codecp);
+                     int do_init, struct hda_codec **codecp);
 
 /*
  * low level functions
@@ -799,11 +856,13 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec);
  * Mixer
  */
 int snd_hda_build_controls(struct hda_bus *bus);
+int snd_hda_codec_build_controls(struct hda_codec *codec);
 
 /*
  * PCM
  */
 int snd_hda_build_pcms(struct hda_bus *bus);
+int snd_hda_codec_build_pcms(struct hda_codec *codec);
 void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
                                u32 stream_tag,
                                int channel_id, int format);
@@ -812,8 +871,6 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
                                        unsigned int channels,
                                        unsigned int format,
                                        unsigned int maxbps);
-int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
-                               u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
 int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
                                unsigned int format);
 
@@ -830,6 +887,13 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state);
 int snd_hda_resume(struct hda_bus *bus);
 #endif
 
+/*
+ * get widget information
+ */
+const char *snd_hda_get_jack_connectivity(u32 cfg);
+const char *snd_hda_get_jack_type(u32 cfg);
+const char *snd_hda_get_jack_location(u32 cfg);
+
 /*
  * power saving
  */
@@ -837,12 +901,25 @@ int snd_hda_resume(struct hda_bus *bus);
 void snd_hda_power_up(struct hda_codec *codec);
 void snd_hda_power_down(struct hda_codec *codec);
 #define snd_hda_codec_needs_resume(codec) codec->power_count
-int snd_hda_codecs_inuse(struct hda_bus *bus);
 #else
 static inline void snd_hda_power_up(struct hda_codec *codec) {}
 static inline void snd_hda_power_down(struct hda_codec *codec) {}
 #define snd_hda_codec_needs_resume(codec) 1
-#define snd_hda_codecs_inuse(bus) 1
+#endif
+
+/*
+ * Codec modularization
+ */
+
+/* Export symbols only for communication with codec drivers;
+ * When built in kernel, all HD-audio drivers are supposed to be statically
+ * linked to the kernel.  Thus, the symbols don't have to (or shouldn't) be
+ * exported unless it's built as a module.
+ */
+#ifdef MODULE
+#define EXPORT_SYMBOL_HDA(sym) EXPORT_SYMBOL_GPL(sym)
+#else
+#define EXPORT_SYMBOL_HDA(sym)
 #endif
 
 #endif /* __SOUND_HDA_CODEC_H */
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
new file mode 100644 (file)
index 0000000..fcad5ec
--- /dev/null
@@ -0,0 +1,590 @@
+/*
+ * Generic routines and proc interface for ELD(EDID Like Data) information
+ *
+ * Copyright(c) 2008 Intel Corporation.
+ *
+ * Authors:
+ *             Wu Fengguang <wfg@linux.intel.com>
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <sound/core.h>
+#include <asm/unaligned.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+enum eld_versions {
+       ELD_VER_CEA_861D        = 2,
+       ELD_VER_PARTIAL         = 31,
+};
+
+enum cea_edid_versions {
+       CEA_EDID_VER_NONE       = 0,
+       CEA_EDID_VER_CEA861     = 1,
+       CEA_EDID_VER_CEA861A    = 2,
+       CEA_EDID_VER_CEA861BCD  = 3,
+       CEA_EDID_VER_RESERVED   = 4,
+};
+
+static char *cea_speaker_allocation_names[] = {
+       /*  0 */ "FL/FR",
+       /*  1 */ "LFE",
+       /*  2 */ "FC",
+       /*  3 */ "RL/RR",
+       /*  4 */ "RC",
+       /*  5 */ "FLC/FRC",
+       /*  6 */ "RLC/RRC",
+       /*  7 */ "FLW/FRW",
+       /*  8 */ "FLH/FRH",
+       /*  9 */ "TC",
+       /* 10 */ "FCH",
+};
+
+static char *eld_connection_type_names[4] = {
+       "HDMI",
+       "DisplayPort",
+       "2-reserved",
+       "3-reserved"
+};
+
+enum cea_audio_coding_types {
+       AUDIO_CODING_TYPE_REF_STREAM_HEADER     =  0,
+       AUDIO_CODING_TYPE_LPCM                  =  1,
+       AUDIO_CODING_TYPE_AC3                   =  2,
+       AUDIO_CODING_TYPE_MPEG1                 =  3,
+       AUDIO_CODING_TYPE_MP3                   =  4,
+       AUDIO_CODING_TYPE_MPEG2                 =  5,
+       AUDIO_CODING_TYPE_AACLC                 =  6,
+       AUDIO_CODING_TYPE_DTS                   =  7,
+       AUDIO_CODING_TYPE_ATRAC                 =  8,
+       AUDIO_CODING_TYPE_SACD                  =  9,
+       AUDIO_CODING_TYPE_EAC3                  = 10,
+       AUDIO_CODING_TYPE_DTS_HD                = 11,
+       AUDIO_CODING_TYPE_MLP                   = 12,
+       AUDIO_CODING_TYPE_DST                   = 13,
+       AUDIO_CODING_TYPE_WMAPRO                = 14,
+       AUDIO_CODING_TYPE_REF_CXT               = 15,
+       /* also include valid xtypes below */
+       AUDIO_CODING_TYPE_HE_AAC                = 15,
+       AUDIO_CODING_TYPE_HE_AAC2               = 16,
+       AUDIO_CODING_TYPE_MPEG_SURROUND         = 17,
+};
+
+enum cea_audio_coding_xtypes {
+       AUDIO_CODING_XTYPE_HE_REF_CT            = 0,
+       AUDIO_CODING_XTYPE_HE_AAC               = 1,
+       AUDIO_CODING_XTYPE_HE_AAC2              = 2,
+       AUDIO_CODING_XTYPE_MPEG_SURROUND        = 3,
+       AUDIO_CODING_XTYPE_FIRST_RESERVED       = 4,
+};
+
+static char *cea_audio_coding_type_names[] = {
+       /*  0 */ "undefined",
+       /*  1 */ "LPCM",
+       /*  2 */ "AC-3",
+       /*  3 */ "MPEG1",
+       /*  4 */ "MP3",
+       /*  5 */ "MPEG2",
+       /*  6 */ "AAC-LC",
+       /*  7 */ "DTS",
+       /*  8 */ "ATRAC",
+       /*  9 */ "DSD (One Bit Audio)",
+       /* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)",
+       /* 11 */ "DTS-HD",
+       /* 12 */ "MLP (Dolby TrueHD)",
+       /* 13 */ "DST",
+       /* 14 */ "WMAPro",
+       /* 15 */ "HE-AAC",
+       /* 16 */ "HE-AACv2",
+       /* 17 */ "MPEG Surround",
+};
+
+/*
+ * The following two lists are shared between
+ *     - HDMI audio InfoFrame (source to sink)
+ *     - CEA E-EDID Extension (sink to source)
+ */
+
+/*
+ * SS1:SS0 index => sample size
+ */
+static int cea_sample_sizes[4] = {
+       0,                      /* 0: Refer to Stream Header */
+       AC_SUPPCM_BITS_16,      /* 1: 16 bits */
+       AC_SUPPCM_BITS_20,      /* 2: 20 bits */
+       AC_SUPPCM_BITS_24,      /* 3: 24 bits */
+};
+
+/*
+ * SF2:SF1:SF0 index => sampling frequency
+ */
+static int cea_sampling_frequencies[8] = {
+       0,                      /* 0: Refer to Stream Header */
+       SNDRV_PCM_RATE_32000,   /* 1:  32000Hz */
+       SNDRV_PCM_RATE_44100,   /* 2:  44100Hz */
+       SNDRV_PCM_RATE_48000,   /* 3:  48000Hz */
+       SNDRV_PCM_RATE_88200,   /* 4:  88200Hz */
+       SNDRV_PCM_RATE_96000,   /* 5:  96000Hz */
+       SNDRV_PCM_RATE_176400,  /* 6: 176400Hz */
+       SNDRV_PCM_RATE_192000,  /* 7: 192000Hz */
+};
+
+static unsigned char hdmi_get_eld_byte(struct hda_codec *codec, hda_nid_t nid,
+                                       int byte_index)
+{
+       unsigned int val;
+
+       val = snd_hda_codec_read(codec, nid, 0,
+                                       AC_VERB_GET_HDMI_ELDD, byte_index);
+
+#ifdef BE_PARANOID
+       printk(KERN_INFO "HDMI: ELD data byte %d: 0x%x\n", byte_index, val);
+#endif
+
+       if ((val & AC_ELDD_ELD_VALID) == 0) {
+               snd_printd(KERN_INFO "HDMI: invalid ELD data byte %d\n",
+                                                               byte_index);
+               val = 0;
+       }
+
+       return val & AC_ELDD_ELD_DATA;
+}
+
+#define GRAB_BITS(buf, byte, lowbit, bits)             \
+({                                                     \
+       BUILD_BUG_ON(lowbit > 7);                       \
+       BUILD_BUG_ON(bits > 8);                         \
+       BUILD_BUG_ON(bits <= 0);                        \
+                                                       \
+       (buf[byte] >> (lowbit)) & ((1 << (bits)) - 1);  \
+})
+
+static void hdmi_update_short_audio_desc(struct cea_sad *a,
+                                        const unsigned char *buf)
+{
+       int i;
+       int val;
+
+       val = GRAB_BITS(buf, 1, 0, 7);
+       a->rates = 0;
+       for (i = 0; i < 7; i++)
+               if (val & (1 << i))
+                       a->rates |= cea_sampling_frequencies[i + 1];
+
+       a->channels = GRAB_BITS(buf, 0, 0, 3);
+       a->channels++;
+
+       a->format = GRAB_BITS(buf, 0, 3, 4);
+       switch (a->format) {
+       case AUDIO_CODING_TYPE_REF_STREAM_HEADER:
+               snd_printd(KERN_INFO
+                               "HDMI: audio coding type 0 not expected\n");
+               break;
+
+       case AUDIO_CODING_TYPE_LPCM:
+               val = GRAB_BITS(buf, 2, 0, 3);
+               a->sample_bits = 0;
+               for (i = 0; i < 3; i++)
+                       if (val & (1 << i))
+                               a->sample_bits |= cea_sample_sizes[i + 1];
+               break;
+
+       case AUDIO_CODING_TYPE_AC3:
+       case AUDIO_CODING_TYPE_MPEG1:
+       case AUDIO_CODING_TYPE_MP3:
+       case AUDIO_CODING_TYPE_MPEG2:
+       case AUDIO_CODING_TYPE_AACLC:
+       case AUDIO_CODING_TYPE_DTS:
+       case AUDIO_CODING_TYPE_ATRAC:
+               a->max_bitrate = GRAB_BITS(buf, 2, 0, 8);
+               a->max_bitrate *= 8000;
+               break;
+
+       case AUDIO_CODING_TYPE_SACD:
+               break;
+
+       case AUDIO_CODING_TYPE_EAC3:
+               break;
+
+       case AUDIO_CODING_TYPE_DTS_HD:
+               break;
+
+       case AUDIO_CODING_TYPE_MLP:
+               break;
+
+       case AUDIO_CODING_TYPE_DST:
+               break;
+
+       case AUDIO_CODING_TYPE_WMAPRO:
+               a->profile = GRAB_BITS(buf, 2, 0, 3);
+               break;
+
+       case AUDIO_CODING_TYPE_REF_CXT:
+               a->format = GRAB_BITS(buf, 2, 3, 5);
+               if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT ||
+                   a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) {
+                       snd_printd(KERN_INFO
+                               "HDMI: audio coding xtype %d not expected\n",
+                               a->format);
+                       a->format = 0;
+               } else
+                       a->format += AUDIO_CODING_TYPE_HE_AAC -
+                                    AUDIO_CODING_XTYPE_HE_AAC;
+               break;
+       }
+}
+
+/*
+ * Be careful, ELD buf could be totally rubbish!
+ */
+static int hdmi_update_eld(struct hdmi_eld *e,
+                          const unsigned char *buf, int size)
+{
+       int mnl;
+       int i;
+
+       e->eld_ver = GRAB_BITS(buf, 0, 3, 5);
+       if (e->eld_ver != ELD_VER_CEA_861D &&
+           e->eld_ver != ELD_VER_PARTIAL) {
+               snd_printd(KERN_INFO "HDMI: Unknown ELD version %d\n",
+                                                               e->eld_ver);
+               goto out_fail;
+       }
+
+       e->eld_size = size;
+       e->baseline_len = GRAB_BITS(buf, 2, 0, 8);
+       mnl             = GRAB_BITS(buf, 4, 0, 5);
+       e->cea_edid_ver = GRAB_BITS(buf, 4, 5, 3);
+
+       e->support_hdcp = GRAB_BITS(buf, 5, 0, 1);
+       e->support_ai   = GRAB_BITS(buf, 5, 1, 1);
+       e->conn_type    = GRAB_BITS(buf, 5, 2, 2);
+       e->sad_count    = GRAB_BITS(buf, 5, 4, 4);
+
+       e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2;
+       e->spk_alloc    = GRAB_BITS(buf, 7, 0, 7);
+
+       e->port_id        = get_unaligned_le64(buf + 8);
+
+       /* not specified, but the spec's tendency is little endian */
+       e->manufacture_id = get_unaligned_le16(buf + 16);
+       e->product_id     = get_unaligned_le16(buf + 18);
+
+       if (mnl > ELD_MAX_MNL) {
+               snd_printd(KERN_INFO "HDMI: MNL is reserved value %d\n", mnl);
+               goto out_fail;
+       } else if (ELD_FIXED_BYTES + mnl > size) {
+               snd_printd(KERN_INFO "HDMI: out of range MNL %d\n", mnl);
+               goto out_fail;
+       } else
+               strlcpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl);
+
+       for (i = 0; i < e->sad_count; i++) {
+               if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) {
+                       snd_printd(KERN_INFO "HDMI: out of range SAD %d\n", i);
+                       goto out_fail;
+               }
+               hdmi_update_short_audio_desc(e->sad + i,
+                                       buf + ELD_FIXED_BYTES + mnl + 3 * i);
+       }
+
+       return 0;
+
+out_fail:
+       e->eld_ver = 0;
+       return -EINVAL;
+}
+
+static int hdmi_present_sense(struct hda_codec *codec, hda_nid_t nid)
+{
+       return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0);
+}
+
+static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid)
+{
+       int eldv;
+       int present;
+
+       present = hdmi_present_sense(codec, nid);
+       eldv    = (present & AC_PINSENSE_ELDV);
+       present = (present & AC_PINSENSE_PRESENCE);
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       printk(KERN_INFO "HDMI: sink_present = %d, eld_valid = %d\n",
+                       !!present, !!eldv);
+#endif
+
+       return eldv && present;
+}
+
+int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
+{
+       return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
+                                                AC_DIPSIZE_ELD_BUF);
+}
+
+int snd_hdmi_get_eld(struct hdmi_eld *eld,
+                    struct hda_codec *codec, hda_nid_t nid)
+{
+       int i;
+       int ret;
+       int size;
+       unsigned char *buf;
+
+       if (!hdmi_eld_valid(codec, nid))
+               return -ENOENT;
+
+       size = snd_hdmi_get_eld_size(codec, nid);
+       if (size == 0) {
+               /* wfg: workaround for ASUS P5E-VM HDMI board */
+               snd_printd(KERN_INFO "HDMI: ELD buf size is 0, force 128\n");
+               size = 128;
+       }
+       if (size < ELD_FIXED_BYTES || size > PAGE_SIZE) {
+               snd_printd(KERN_INFO "HDMI: invalid ELD buf size %d\n", size);
+               return -ERANGE;
+       }
+
+       buf = kmalloc(size, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       for (i = 0; i < size; i++)
+               buf[i] = hdmi_get_eld_byte(codec, nid, i);
+
+       ret = hdmi_update_eld(eld, buf, size);
+
+       kfree(buf);
+       return ret;
+}
+
+static void hdmi_show_short_audio_desc(struct cea_sad *a)
+{
+       char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
+       char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits =";
+
+       if (!a->format)
+               return;
+
+       snd_print_pcm_rates(a->rates, buf, sizeof(buf));
+
+       if (a->format == AUDIO_CODING_TYPE_LPCM)
+               snd_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2 - 8));
+       else if (a->max_bitrate)
+               snprintf(buf2, sizeof(buf2),
+                               ", max bitrate = %d", a->max_bitrate);
+       else
+               buf2[0] = '\0';
+
+       printk(KERN_INFO "HDMI: supports coding type %s:"
+                       " channels = %d, rates =%s%s\n",
+                       cea_audio_coding_type_names[a->format],
+                       a->channels,
+                       buf,
+                       buf2);
+}
+
+void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
+{
+       int i, j;
+
+       for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
+               if (spk_alloc & (1 << i))
+                       j += snprintf(buf + j, buflen - j,  " %s",
+                                       cea_speaker_allocation_names[i]);
+       }
+       buf[j] = '\0';  /* necessary when j == 0 */
+}
+
+void snd_hdmi_show_eld(struct hdmi_eld *e)
+{
+       int i;
+
+       printk(KERN_INFO "HDMI: detected monitor %s at connection type %s\n",
+                       e->monitor_name,
+                       eld_connection_type_names[e->conn_type]);
+
+       if (e->spk_alloc) {
+               char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+               snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+               printk(KERN_INFO "HDMI: available speakers:%s\n", buf);
+       }
+
+       for (i = 0; i < e->sad_count; i++)
+               hdmi_show_short_audio_desc(e->sad + i);
+}
+
+#ifdef CONFIG_PROC_FS
+
+static void hdmi_print_sad_info(int i, struct cea_sad *a,
+                               struct snd_info_buffer *buffer)
+{
+       char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
+
+       snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s\n",
+                       i, a->format, cea_audio_coding_type_names[a->format]);
+       snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels);
+
+       snd_print_pcm_rates(a->rates, buf, sizeof(buf));
+       snd_iprintf(buffer, "sad%d_rates\t\t[0x%x]%s\n", i, a->rates, buf);
+
+       if (a->format == AUDIO_CODING_TYPE_LPCM) {
+               snd_print_pcm_bits(a->sample_bits, buf, sizeof(buf));
+               snd_iprintf(buffer, "sad%d_bits\t\t[0x%x]%s\n",
+                                                       i, a->sample_bits, buf);
+       }
+
+       if (a->max_bitrate)
+               snd_iprintf(buffer, "sad%d_max_bitrate\t%d\n",
+                                                       i, a->max_bitrate);
+
+       if (a->profile)
+               snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile);
+}
+
+static void hdmi_print_eld_info(struct snd_info_entry *entry,
+                               struct snd_info_buffer *buffer)
+{
+       struct hdmi_eld *e = entry->private_data;
+       char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+       int i;
+       static char *eld_versoin_names[32] = {
+               "reserved",
+               "reserved",
+               "CEA-861D or below",
+               [3 ... 30] = "reserved",
+               [31] = "partial"
+       };
+       static char *cea_edid_version_names[8] = {
+               "no CEA EDID Timing Extension block present",
+               "CEA-861",
+               "CEA-861-A",
+               "CEA-861-B, C or D",
+               [4 ... 7] = "reserved"
+       };
+
+       snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name);
+       snd_iprintf(buffer, "connection_type\t\t%s\n",
+                               eld_connection_type_names[e->conn_type]);
+       snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver,
+                                       eld_versoin_names[e->eld_ver]);
+       snd_iprintf(buffer, "edid_version\t\t[0x%x] %s\n", e->cea_edid_ver,
+                               cea_edid_version_names[e->cea_edid_ver]);
+       snd_iprintf(buffer, "manufacture_id\t\t0x%x\n", e->manufacture_id);
+       snd_iprintf(buffer, "product_id\t\t0x%x\n", e->product_id);
+       snd_iprintf(buffer, "port_id\t\t\t0x%llx\n", (long long)e->port_id);
+       snd_iprintf(buffer, "support_hdcp\t\t%d\n", e->support_hdcp);
+       snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai);
+       snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay);
+
+       snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+       snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf);
+
+       snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count);
+
+       for (i = 0; i < e->sad_count; i++)
+               hdmi_print_sad_info(i, e->sad + i, buffer);
+}
+
+static void hdmi_write_eld_info(struct snd_info_entry *entry,
+                               struct snd_info_buffer *buffer)
+{
+       struct hdmi_eld *e = entry->private_data;
+       char line[64];
+       char name[64];
+       char *sname;
+       long long val;
+       int n;
+
+       while (!snd_info_get_line(buffer, line, sizeof(line))) {
+               if (sscanf(line, "%s %llx", name, &val) != 2)
+                       continue;
+               /*
+                * We don't allow modification to these fields:
+                *      monitor_name manufacture_id product_id
+                *      eld_version edid_version
+                */
+               if (!strcmp(name, "connection_type"))
+                       e->conn_type = val;
+               else if (!strcmp(name, "port_id"))
+                       e->port_id = val;
+               else if (!strcmp(name, "support_hdcp"))
+                       e->support_hdcp = val;
+               else if (!strcmp(name, "support_ai"))
+                       e->support_ai = val;
+               else if (!strcmp(name, "audio_sync_delay"))
+                       e->aud_synch_delay = val;
+               else if (!strcmp(name, "speakers"))
+                       e->spk_alloc = val;
+               else if (!strcmp(name, "sad_count"))
+                       e->sad_count = val;
+               else if (!strncmp(name, "sad", 3)) {
+                       sname = name + 4;
+                       n = name[3] - '0';
+                       if (name[4] >= '0' && name[4] <= '9') {
+                               sname++;
+                               n = 10 * n + name[4] - '0';
+                       }
+                       if (n < 0 || n > 31) /* double the CEA limit */
+                               continue;
+                       if (!strcmp(sname, "_coding_type"))
+                               e->sad[n].format = val;
+                       else if (!strcmp(sname, "_channels"))
+                               e->sad[n].channels = val;
+                       else if (!strcmp(sname, "_rates"))
+                               e->sad[n].rates = val;
+                       else if (!strcmp(sname, "_bits"))
+                               e->sad[n].sample_bits = val;
+                       else if (!strcmp(sname, "_max_bitrate"))
+                               e->sad[n].max_bitrate = val;
+                       else if (!strcmp(sname, "_profile"))
+                               e->sad[n].profile = val;
+                       if (n >= e->sad_count)
+                               e->sad_count = n + 1;
+               }
+       }
+}
+
+
+int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld)
+{
+       char name[32];
+       struct snd_info_entry *entry;
+       int err;
+
+       snprintf(name, sizeof(name), "eld#%d", codec->addr);
+       err = snd_card_proc_new(codec->bus->card, name, &entry);
+       if (err < 0)
+               return err;
+
+       snd_info_set_text_ops(entry, eld, hdmi_print_eld_info);
+       entry->c.text.write = hdmi_write_eld_info;
+       entry->mode |= S_IWUSR;
+       eld->proc_entry = entry;
+
+       return 0;
+}
+
+void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
+{
+       if (!codec->bus->shutdown && eld->proc_entry) {
+               snd_device_free(codec->bus->card, eld->proc_entry);
+               eld->proc_entry = NULL;
+       }
+}
+
+#endif /* CONFIG_PROC_FS */
index 0ca3089..65745e9 100644 (file)
@@ -723,7 +723,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
                if (is_loopback)
                        add_input_loopback(codec, node->nid, HDA_INPUT, index);
                snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
-               if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+               err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+               if (err < 0)
                        return err;
                created = 1;
        } else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
@@ -732,7 +733,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
                if (is_loopback)
                        add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
                snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
-               if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+               err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+               if (err < 0)
                        return err;
                created = 1;
        }
@@ -745,14 +747,16 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
            (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
                knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
                snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
-               if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+               err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+               if (err < 0)
                        return err;
                created = 1;
        } else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
                   (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
                knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
                snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
-               if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+               err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+               if (err < 0)
                        return err;
                created = 1;
        }
@@ -849,8 +853,8 @@ static int build_input_controls(struct hda_codec *codec)
        }
 
        /* create input MUX if multiple sources are available */
-       if ((err = snd_ctl_add(codec->bus->card,
-                              snd_ctl_new1(&cap_sel, codec))) < 0)
+       err = snd_hda_ctl_add(codec, snd_ctl_new1(&cap_sel, codec));
+       if (err < 0)
                return err;
 
        /* no volume control? */
@@ -867,8 +871,8 @@ static int build_input_controls(struct hda_codec *codec)
                        HDA_CODEC_VOLUME(name, adc_node->nid,
                                         spec->input_mux.items[i].index,
                                         HDA_INPUT);
-               if ((err = snd_ctl_add(codec->bus->card,
-                                      snd_ctl_new1(&knew, codec))) < 0)
+               err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+               if (err < 0)
                        return err;
        }
 
@@ -1097,3 +1101,4 @@ int snd_hda_parse_generic_codec(struct hda_codec *codec)
        snd_hda_generic_free(codec);
        return err;
 }
+EXPORT_SYMBOL(snd_hda_parse_generic_codec);
index 6e18a42..300ab40 100644 (file)
 #include <linux/pci.h>
 #include <linux/compat.h>
 #include <linux/mutex.h>
+#include <linux/ctype.h>
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
 #include <sound/hda_hwdep.h>
+#include <sound/minors.h>
 
 /*
  * write/read an out-of-bound verb
@@ -95,7 +97,26 @@ static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
        return 0;
 }
 
-int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
+static void clear_hwdep_elements(struct hda_codec *codec)
+{
+       char **head;
+       int i;
+
+       /* clear init verbs */
+       snd_array_free(&codec->init_verbs);
+       /* clear hints */
+       head = codec->hints.list;
+       for (i = 0; i < codec->hints.used; i++, head++)
+               kfree(*head);
+       snd_array_free(&codec->hints);
+}
+
+static void hwdep_free(struct snd_hwdep *hwdep)
+{
+       clear_hwdep_elements(hwdep->private_data);
+}
+
+int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
 {
        char hwname[16];
        struct snd_hwdep *hwdep;
@@ -109,6 +130,7 @@ int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
        sprintf(hwdep->name, "HDA Codec %d", codec->addr);
        hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
        hwdep->private_data = codec;
+       hwdep->private_free = hwdep_free;
        hwdep->exclusive = 1;
 
        hwdep->ops.open = hda_hwdep_open;
@@ -117,5 +139,215 @@ int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
        hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
 #endif
 
+       snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
+       snd_array_init(&codec->hints, sizeof(char *), 32);
+
        return 0;
 }
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+
+/*
+ * sysfs interface
+ */
+
+static int clear_codec(struct hda_codec *codec)
+{
+       snd_hda_codec_reset(codec);
+       clear_hwdep_elements(codec);
+       return 0;
+}
+
+static int reconfig_codec(struct hda_codec *codec)
+{
+       int err;
+
+       snd_printk(KERN_INFO "hda-codec: reconfiguring\n");
+       snd_hda_codec_reset(codec);
+       err = snd_hda_codec_configure(codec);
+       if (err < 0)
+               return err;
+       /* rebuild PCMs */
+       err = snd_hda_codec_build_pcms(codec);
+       if (err < 0)
+               return err;
+       /* rebuild mixers */
+       err = snd_hda_codec_build_controls(codec);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+/*
+ * allocate a string at most len chars, and remove the trailing EOL
+ */
+static char *kstrndup_noeol(const char *src, size_t len)
+{
+       char *s = kstrndup(src, len, GFP_KERNEL);
+       char *p;
+       if (!s)
+               return NULL;
+       p = strchr(s, '\n');
+       if (p)
+               *p = 0;
+       return s;
+}
+
+#define CODEC_INFO_SHOW(type)                                  \
+static ssize_t type##_show(struct device *dev,                 \
+                          struct device_attribute *attr,       \
+                          char *buf)                           \
+{                                                              \
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);         \
+       struct hda_codec *codec = hwdep->private_data;          \
+       return sprintf(buf, "0x%x\n", codec->type);             \
+}
+
+#define CODEC_INFO_STR_SHOW(type)                              \
+static ssize_t type##_show(struct device *dev,                 \
+                            struct device_attribute *attr,     \
+                                       char *buf)              \
+{                                                              \
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);         \
+       struct hda_codec *codec = hwdep->private_data;          \
+       return sprintf(buf, "%s\n",                             \
+                      codec->type ? codec->type : "");         \
+}
+
+CODEC_INFO_SHOW(vendor_id);
+CODEC_INFO_SHOW(subsystem_id);
+CODEC_INFO_SHOW(revision_id);
+CODEC_INFO_SHOW(afg);
+CODEC_INFO_SHOW(mfg);
+CODEC_INFO_STR_SHOW(name);
+CODEC_INFO_STR_SHOW(modelname);
+
+#define CODEC_INFO_STORE(type)                                 \
+static ssize_t type##_store(struct device *dev,                        \
+                           struct device_attribute *attr,      \
+                           const char *buf, size_t count)      \
+{                                                              \
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);         \
+       struct hda_codec *codec = hwdep->private_data;          \
+       char *after;                                            \
+       codec->type = simple_strtoul(buf, &after, 0);           \
+       return count;                                           \
+}
+
+#define CODEC_INFO_STR_STORE(type)                             \
+static ssize_t type##_store(struct device *dev,                        \
+                           struct device_attribute *attr,      \
+                           const char *buf, size_t count)      \
+{                                                              \
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);         \
+       struct hda_codec *codec = hwdep->private_data;          \
+       char *s = kstrndup_noeol(buf, 64);                      \
+       if (!s)                                                 \
+               return -ENOMEM;                                 \
+       kfree(codec->type);                                     \
+       codec->type = s;                                        \
+       return count;                                           \
+}
+
+CODEC_INFO_STORE(vendor_id);
+CODEC_INFO_STORE(subsystem_id);
+CODEC_INFO_STORE(revision_id);
+CODEC_INFO_STR_STORE(name);
+CODEC_INFO_STR_STORE(modelname);
+
+#define CODEC_ACTION_STORE(type)                               \
+static ssize_t type##_store(struct device *dev,                        \
+                           struct device_attribute *attr,      \
+                           const char *buf, size_t count)      \
+{                                                              \
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);         \
+       struct hda_codec *codec = hwdep->private_data;          \
+       int err = 0;                                            \
+       if (*buf)                                               \
+               err = type##_codec(codec);                      \
+       return err < 0 ? err : count;                           \
+}
+
+CODEC_ACTION_STORE(reconfig);
+CODEC_ACTION_STORE(clear);
+
+static ssize_t init_verbs_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+       struct hda_codec *codec = hwdep->private_data;
+       char *p;
+       struct hda_verb verb, *v;
+
+       verb.nid = simple_strtoul(buf, &p, 0);
+       verb.verb = simple_strtoul(p, &p, 0);
+       verb.param = simple_strtoul(p, &p, 0);
+       if (!verb.nid || !verb.verb || !verb.param)
+               return -EINVAL;
+       v = snd_array_new(&codec->init_verbs);
+       if (!v)
+               return -ENOMEM;
+       *v = verb;
+       return count;
+}
+
+static ssize_t hints_store(struct device *dev,
+                          struct device_attribute *attr,
+                          const char *buf, size_t count)
+{
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+       struct hda_codec *codec = hwdep->private_data;
+       char *p;
+       char **hint;
+
+       if (!*buf || isspace(*buf) || *buf == '#' || *buf == '\n')
+               return count;
+       p = kstrndup_noeol(buf, 1024);
+       if (!p)
+               return -ENOMEM;
+       hint = snd_array_new(&codec->hints);
+       if (!hint) {
+               kfree(p);
+               return -ENOMEM;
+       }
+       *hint = p;
+       return count;
+}
+
+#define CODEC_ATTR_RW(type) \
+       __ATTR(type, 0644, type##_show, type##_store)
+#define CODEC_ATTR_RO(type) \
+       __ATTR_RO(type)
+#define CODEC_ATTR_WO(type) \
+       __ATTR(type, 0200, NULL, type##_store)
+
+static struct device_attribute codec_attrs[] = {
+       CODEC_ATTR_RW(vendor_id),
+       CODEC_ATTR_RW(subsystem_id),
+       CODEC_ATTR_RW(revision_id),
+       CODEC_ATTR_RO(afg),
+       CODEC_ATTR_RO(mfg),
+       CODEC_ATTR_RW(name),
+       CODEC_ATTR_RW(modelname),
+       CODEC_ATTR_WO(init_verbs),
+       CODEC_ATTR_WO(hints),
+       CODEC_ATTR_WO(reconfig),
+       CODEC_ATTR_WO(clear),
+};
+
+/*
+ * create sysfs files on hwdep directory
+ */
+int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
+{
+       struct snd_hwdep *hwdep = codec->hwdep;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(codec_attrs); i++)
+               snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
+                                         hwdep->device, &codec_attrs[i]);
+       return 0;
+}
+
+#endif /* CONFIG_SND_HDA_RECONFIG */
index 35722ec..f04de11 100644 (file)
@@ -58,6 +58,7 @@ static char *model[SNDRV_CARDS];
 static int position_fix[SNDRV_CARDS];
 static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
 static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
+static int probe_only[SNDRV_CARDS];
 static int single_cmd;
 static int enable_msi;
 
@@ -76,6 +77,8 @@ module_param_array(bdl_pos_adj, int, NULL, 0644);
 MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
 module_param_array(probe_mask, int, NULL, 0444);
 MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
+module_param_array(probe_only, bool, NULL, 0444);
+MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization.");
 module_param(single_cmd, bool, 0444);
 MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
                 "(for debugging only).");
@@ -83,7 +86,10 @@ module_param(enable_msi, int, 0444);
 MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-/* power_save option is defined in hda_codec.c */
+static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
+module_param(power_save, int, 0644);
+MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
+                "(in second, 0 = disable).");
 
 /* reset the HD-audio controller in power save mode.
  * this may give more power-saving, but will take longer time to
@@ -292,6 +298,8 @@ enum {
 /* Define VIA HD Audio Device ID*/
 #define VIA_HDAC_DEVICE_ID             0x3288
 
+/* HD Audio class code */
+#define PCI_CLASS_MULTIMEDIA_HD_AUDIO  0x0403
 
 /*
  */
@@ -392,6 +400,7 @@ struct azx {
        unsigned int msi :1;
        unsigned int irq_pending_warned :1;
        unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */
+       unsigned int probing :1; /* codec probing phase */
 
        /* for debugging */
        unsigned int last_cmd;  /* last issued command (to sync) */
@@ -414,6 +423,7 @@ enum {
        AZX_DRIVER_ULI,
        AZX_DRIVER_NVIDIA,
        AZX_DRIVER_TERA,
+       AZX_DRIVER_GENERIC,
        AZX_NUM_DRIVERS, /* keep this as last entry */
 };
 
@@ -427,6 +437,7 @@ static char *driver_short_names[] __devinitdata = {
        [AZX_DRIVER_ULI] = "HDA ULI M5461",
        [AZX_DRIVER_NVIDIA] = "HDA NVidia",
        [AZX_DRIVER_TERA] = "HDA Teradici", 
+       [AZX_DRIVER_GENERIC] = "HD-Audio Generic",
 };
 
 /*
@@ -527,9 +538,9 @@ static void azx_free_cmd_io(struct azx *chip)
 }
 
 /* send a command */
-static int azx_corb_send_cmd(struct hda_codec *codec, u32 val)
+static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
 {
-       struct azx *chip = codec->bus->private_data;
+       struct azx *chip = bus->private_data;
        unsigned int wp;
 
        /* add command to corb */
@@ -577,9 +588,9 @@ static void azx_update_rirb(struct azx *chip)
 }
 
 /* receive a response */
-static unsigned int azx_rirb_get_response(struct hda_codec *codec)
+static unsigned int azx_rirb_get_response(struct hda_bus *bus)
 {
-       struct azx *chip = codec->bus->private_data;
+       struct azx *chip = bus->private_data;
        unsigned long timeout;
 
  again:
@@ -596,7 +607,7 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
                }
                if (time_after(jiffies, timeout))
                        break;
-               if (codec->bus->needs_damn_long_delay)
+               if (bus->needs_damn_long_delay)
                        msleep(2); /* temporary workaround */
                else {
                        udelay(10);
@@ -624,6 +635,14 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
                goto again;
        }
 
+       if (chip->probing) {
+               /* If this critical timeout happens during the codec probing
+                * phase, this is likely an access to a non-existing codec
+                * slot.  Better to return an error and reset the system.
+                */
+               return -1;
+       }
+
        snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
                   "switching to single_cmd mode: last cmd=0x%08x\n",
                   chip->last_cmd);
@@ -646,9 +665,9 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
  */
 
 /* send a command */
-static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
+static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
 {
-       struct azx *chip = codec->bus->private_data;
+       struct azx *chip = bus->private_data;
        int timeout = 50;
 
        while (timeout--) {
@@ -671,9 +690,9 @@ static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
 }
 
 /* receive a response */
-static unsigned int azx_single_get_response(struct hda_codec *codec)
+static unsigned int azx_single_get_response(struct hda_bus *bus)
 {
-       struct azx *chip = codec->bus->private_data;
+       struct azx *chip = bus->private_data;
        int timeout = 50;
 
        while (timeout--) {
@@ -696,38 +715,29 @@ static unsigned int azx_single_get_response(struct hda_codec *codec)
  */
 
 /* send a command */
-static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid,
-                       int direct, unsigned int verb,
-                       unsigned int para)
+static int azx_send_cmd(struct hda_bus *bus, unsigned int val)
 {
-       struct azx *chip = codec->bus->private_data;
-       u32 val;
-
-       val = (u32)(codec->addr & 0x0f) << 28;
-       val |= (u32)direct << 27;
-       val |= (u32)nid << 20;
-       val |= verb << 8;
-       val |= para;
-       chip->last_cmd = val;
+       struct azx *chip = bus->private_data;
 
+       chip->last_cmd = val;
        if (chip->single_cmd)
-               return azx_single_send_cmd(codec, val);
+               return azx_single_send_cmd(bus, val);
        else
-               return azx_corb_send_cmd(codec, val);
+               return azx_corb_send_cmd(bus, val);
 }
 
 /* get a response */
-static unsigned int azx_get_response(struct hda_codec *codec)
+static unsigned int azx_get_response(struct hda_bus *bus)
 {
-       struct azx *chip = codec->bus->private_data;
+       struct azx *chip = bus->private_data;
        if (chip->single_cmd)
-               return azx_single_get_response(codec);
+               return azx_single_get_response(bus);
        else
-               return azx_rirb_get_response(codec);
+               return azx_rirb_get_response(bus);
 }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-static void azx_power_notify(struct hda_codec *codec);
+static void azx_power_notify(struct hda_bus *bus);
 #endif
 
 /* reset codec link */
@@ -1184,6 +1194,28 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
        return 0;
 }
 
+/*
+ * Probe the given codec address
+ */
+static int probe_codec(struct azx *chip, int addr)
+{
+       unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
+               (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
+       unsigned int res;
+
+       chip->probing = 1;
+       azx_send_cmd(chip->bus, cmd);
+       res = azx_get_response(chip->bus);
+       chip->probing = 0;
+       if (res == -1)
+               return -EIO;
+       snd_printdd("hda_intel: codec #%d probed OK\n", addr);
+       return 0;
+}
+
+static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
+                                struct hda_pcm *cpcm);
+static void azx_stop_chip(struct azx *chip);
 
 /*
  * Codec initialization
@@ -1194,21 +1226,13 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
        [AZX_DRIVER_TERA] = 1,
 };
 
-/* number of slots to probe as default
- * this can be different from azx_max_codecs[] -- e.g. some boards
- * report wrongly the non-existing 4th slot availability
- */
-static unsigned int azx_default_codecs[AZX_NUM_DRIVERS] __devinitdata = {
-       [AZX_DRIVER_ICH] = 3,
-       [AZX_DRIVER_ATI] = 3,
-};
-
 static int __devinit azx_codec_create(struct azx *chip, const char *model,
-                                     unsigned int codec_probe_mask)
+                                     unsigned int codec_probe_mask,
+                                     int no_init)
 {
        struct hda_bus_template bus_temp;
-       int c, codecs, audio_codecs, err;
-       int def_slots, max_slots;
+       int c, codecs, err;
+       int max_slots;
 
        memset(&bus_temp, 0, sizeof(bus_temp));
        bus_temp.private_data = chip;
@@ -1216,7 +1240,9 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
        bus_temp.pci = chip->pci;
        bus_temp.ops.command = azx_send_cmd;
        bus_temp.ops.get_response = azx_get_response;
+       bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
+       bus_temp.power_save = &power_save;
        bus_temp.ops.pm_notify = azx_power_notify;
 #endif
 
@@ -1227,33 +1253,43 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
        if (chip->driver_type == AZX_DRIVER_NVIDIA)
                chip->bus->needs_damn_long_delay = 1;
 
-       codecs = audio_codecs = 0;
+       codecs = 0;
        max_slots = azx_max_codecs[chip->driver_type];
        if (!max_slots)
                max_slots = AZX_MAX_CODECS;
-       def_slots = azx_default_codecs[chip->driver_type];
-       if (!def_slots)
-               def_slots = max_slots;
-       for (c = 0; c < def_slots; c++) {
+
+       /* First try to probe all given codec slots */
+       for (c = 0; c < max_slots; c++) {
+               if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
+                       if (probe_codec(chip, c) < 0) {
+                               /* Some BIOSen give you wrong codec addresses
+                                * that don't exist
+                                */
+                               snd_printk(KERN_WARNING
+                                          "hda_intel: Codec #%d probe error; "
+                                          "disabling it...\n", c);
+                               chip->codec_mask &= ~(1 << c);
+                               /* More badly, accessing to a non-existing
+                                * codec often screws up the controller chip,
+                                * and distrubs the further communications.
+                                * Thus if an error occurs during probing,
+                                * better to reset the controller chip to
+                                * get back to the sanity state.
+                                */
+                               azx_stop_chip(chip);
+                               azx_init_chip(chip);
+                       }
+               }
+       }
+
+       /* Then create codec instances */
+       for (c = 0; c < max_slots; c++) {
                if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
                        struct hda_codec *codec;
-                       err = snd_hda_codec_new(chip->bus, c, &codec);
+                       err = snd_hda_codec_new(chip->bus, c, !no_init, &codec);
                        if (err < 0)
                                continue;
                        codecs++;
-                       if (codec->afg)
-                               audio_codecs++;
-               }
-       }
-       if (!audio_codecs) {
-               /* probe additional slots if no codec is found */
-               for (; c < max_slots; c++) {
-                       if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
-                               err = snd_hda_codec_new(chip->bus, c, NULL);
-                               if (err < 0)
-                                       continue;
-                               codecs++;
-                       }
                }
        }
        if (!codecs) {
@@ -1722,111 +1758,59 @@ static struct snd_pcm_ops azx_pcm_ops = {
 
 static void azx_pcm_free(struct snd_pcm *pcm)
 {
-       kfree(pcm->private_data);
+       struct azx_pcm *apcm = pcm->private_data;
+       if (apcm) {
+               apcm->chip->pcm[pcm->device] = NULL;
+               kfree(apcm);
+       }
 }
 
-static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
-                                     struct hda_pcm *cpcm)
+static int
+azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
+                     struct hda_pcm *cpcm)
 {
-       int err;
+       struct azx *chip = bus->private_data;
        struct snd_pcm *pcm;
        struct azx_pcm *apcm;
+       int pcm_dev = cpcm->device;
+       int s, err;
 
-       /* if no substreams are defined for both playback and capture,
-        * it's just a placeholder.  ignore it.
-        */
-       if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
-               return 0;
-
-       if (snd_BUG_ON(!cpcm->name))
+       if (pcm_dev >= AZX_MAX_PCMS) {
+               snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n",
+                          pcm_dev);
                return -EINVAL;
-
-       err = snd_pcm_new(chip->card, cpcm->name, cpcm->device,
-                         cpcm->stream[0].substreams,
-                         cpcm->stream[1].substreams,
+       }
+       if (chip->pcm[pcm_dev]) {
+               snd_printk(KERN_ERR SFX "PCM %d already exists\n", pcm_dev);
+               return -EBUSY;
+       }
+       err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
+                         cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams,
+                         cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams,
                          &pcm);
        if (err < 0)
                return err;
        strcpy(pcm->name, cpcm->name);
-       apcm = kmalloc(sizeof(*apcm), GFP_KERNEL);
+       apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
        if (apcm == NULL)
                return -ENOMEM;
        apcm->chip = chip;
        apcm->codec = codec;
-       apcm->hinfo[0] = &cpcm->stream[0];
-       apcm->hinfo[1] = &cpcm->stream[1];
        pcm->private_data = apcm;
        pcm->private_free = azx_pcm_free;
-       if (cpcm->stream[0].substreams)
-               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops);
-       if (cpcm->stream[1].substreams)
-               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);
+       if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
+               pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
+       chip->pcm[pcm_dev] = pcm;
+       cpcm->pcm = pcm;
+       for (s = 0; s < 2; s++) {
+               apcm->hinfo[s] = &cpcm->stream[s];
+               if (cpcm->stream[s].substreams)
+                       snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
+       }
+       /* buffer pre-allocation */
        snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
                                              snd_dma_pci_data(chip->pci),
                                              1024 * 64, 32 * 1024 * 1024);
-       chip->pcm[cpcm->device] = pcm;
-       return 0;
-}
-
-static int __devinit azx_pcm_create(struct azx *chip)
-{
-       static const char *dev_name[HDA_PCM_NTYPES] = {
-               "Audio", "SPDIF", "HDMI", "Modem"
-       };
-       /* starting device index for each PCM type */
-       static int dev_idx[HDA_PCM_NTYPES] = {
-               [HDA_PCM_TYPE_AUDIO] = 0,
-               [HDA_PCM_TYPE_SPDIF] = 1,
-               [HDA_PCM_TYPE_HDMI] = 3,
-               [HDA_PCM_TYPE_MODEM] = 6
-       };
-       /* normal audio device indices; not linear to keep compatibility */
-       static int audio_idx[4] = { 0, 2, 4, 5 };
-       struct hda_codec *codec;
-       int c, err;
-       int num_devs[HDA_PCM_NTYPES];
-
-       err = snd_hda_build_pcms(chip->bus);
-       if (err < 0)
-               return err;
-
-       /* create audio PCMs */
-       memset(num_devs, 0, sizeof(num_devs));
-       list_for_each_entry(codec, &chip->bus->codec_list, list) {
-               for (c = 0; c < codec->num_pcms; c++) {
-                       struct hda_pcm *cpcm = &codec->pcm_info[c];
-                       int type = cpcm->pcm_type;
-                       switch (type) {
-                       case HDA_PCM_TYPE_AUDIO:
-                               if (num_devs[type] >= ARRAY_SIZE(audio_idx)) {
-                                       snd_printk(KERN_WARNING
-                                                  "Too many audio devices\n");
-                                       continue;
-                               }
-                               cpcm->device = audio_idx[num_devs[type]];
-                               break;
-                       case HDA_PCM_TYPE_SPDIF:
-                       case HDA_PCM_TYPE_HDMI:
-                       case HDA_PCM_TYPE_MODEM:
-                               if (num_devs[type]) {
-                                       snd_printk(KERN_WARNING
-                                                  "%s already defined\n",
-                                                  dev_name[type]);
-                                       continue;
-                               }
-                               cpcm->device = dev_idx[type];
-                               break;
-                       default:
-                               snd_printk(KERN_WARNING
-                                          "Invalid PCM type %d\n", type);
-                               continue;
-                       }
-                       num_devs[type]++;
-                       err = create_codec_pcm(chip, codec, cpcm);
-                       if (err < 0)
-                               return err;
-               }
-       }
        return 0;
 }
 
@@ -1903,13 +1887,13 @@ static void azx_stop_chip(struct azx *chip)
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 /* power-up/down the controller */
-static void azx_power_notify(struct hda_codec *codec)
+static void azx_power_notify(struct hda_bus *bus)
 {
-       struct azx *chip = codec->bus->private_data;
+       struct azx *chip = bus->private_data;
        struct hda_codec *c;
        int power_on = 0;
 
-       list_for_each_entry(c, &codec->bus->codec_list, list) {
+       list_for_each_entry(c, &bus->codec_list, list) {
                if (c->power_on) {
                        power_on = 1;
                        break;
@@ -1926,6 +1910,18 @@ static void azx_power_notify(struct hda_codec *codec)
 /*
  * power management
  */
+
+static int snd_hda_codecs_inuse(struct hda_bus *bus)
+{
+       struct hda_codec *codec;
+
+       list_for_each_entry(codec, &bus->codec_list, list) {
+               if (snd_hda_codec_needs_resume(codec))
+                       return 1;
+       }
+       return 0;
+}
+
 static int azx_suspend(struct pci_dev *pci, pm_message_t state)
 {
        struct snd_card *card = pci_get_drvdata(pci);
@@ -1951,13 +1947,16 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
        return 0;
 }
 
+static int azx_resume_early(struct pci_dev *pci)
+{
+       return pci_restore_state(pci);
+}
+
 static int azx_resume(struct pci_dev *pci)
 {
        struct snd_card *card = pci_get_drvdata(pci);
        struct azx *chip = card->private_data;
 
-       pci_set_power_state(pci, PCI_D0);
-       pci_restore_state(pci);
        if (pci_enable_device(pci) < 0) {
                printk(KERN_ERR "hda-intel: pci_enable_device failed, "
                       "disabling device\n");
@@ -2095,6 +2094,10 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
        SND_PCI_QUIRK(0x1014, 0x05b7, "Thinkpad Z60", 0x01),
        SND_PCI_QUIRK(0x17aa, 0x2010, "Thinkpad X/T/R60", 0x01),
        SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X/T/R61", 0x01),
+       /* broken BIOS */
+       SND_PCI_QUIRK(0x1028, 0x20ac, "Dell Studio Desktop", 0x01),
+       /* including bogus ALC268 in slot#2 that conflicts with ALC888 */
+       SND_PCI_QUIRK(0x17c0, 0x4085, "Medion MD96630", 0x01),
        {}
 };
 
@@ -2229,6 +2232,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
                        chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
                        chip->capture_streams = ATIHDMI_NUM_CAPTURE;
                        break;
+               case AZX_DRIVER_GENERIC:
                default:
                        chip->playback_streams = ICH6_NUM_PLAYBACK;
                        chip->capture_streams = ICH6_NUM_CAPTURE;
@@ -2338,40 +2342,31 @@ static int __devinit azx_probe(struct pci_dev *pci,
        }
 
        err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
-       if (err < 0) {
-               snd_card_free(card);
-               return err;
-       }
+       if (err < 0)
+               goto out_free;
        card->private_data = chip;
 
        /* create codec instances */
-       err = azx_codec_create(chip, model[dev], probe_mask[dev]);
-       if (err < 0) {
-               snd_card_free(card);
-               return err;
-       }
+       err = azx_codec_create(chip, model[dev], probe_mask[dev],
+                              probe_only[dev]);
+       if (err < 0)
+               goto out_free;
 
        /* create PCM streams */
-       err = azx_pcm_create(chip);
-       if (err < 0) {
-               snd_card_free(card);
-               return err;
-       }
+       err = snd_hda_build_pcms(chip->bus);
+       if (err < 0)
+               goto out_free;
 
        /* create mixer controls */
        err = azx_mixer_create(chip);
-       if (err < 0) {
-               snd_card_free(card);
-               return err;
-       }
+       if (err < 0)
+               goto out_free;
 
        snd_card_set_dev(card, &pci->dev);
 
        err = snd_card_register(card);
-       if (err < 0) {
-               snd_card_free(card);
-               return err;
-       }
+       if (err < 0)
+               goto out_free;
 
        pci_set_drvdata(pci, card);
        chip->running = 1;
@@ -2380,6 +2375,9 @@ static int __devinit azx_probe(struct pci_dev *pci,
 
        dev++;
        return err;
+out_free:
+       snd_card_free(card);
+       return err;
 }
 
 static void __devexit azx_remove(struct pci_dev *pci)
@@ -2453,6 +2451,11 @@ static struct pci_device_id azx_ids[] = {
        { PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA },
        /* Teradici */
        { PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
+       /* AMD Generic, PCI class code and Vendor ID for HD Audio */
+       { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
+         .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+         .class_mask = 0xffffff,
+         .driver_data = AZX_DRIVER_GENERIC },
        { 0, }
 };
 MODULE_DEVICE_TABLE(pci, azx_ids);
@@ -2465,6 +2468,7 @@ static struct pci_driver driver = {
        .remove = __devexit_p(azx_remove),
 #ifdef CONFIG_PM
        .suspend = azx_suspend,
+       .resume_early = azx_resume_early,
        .resume = azx_resume,
 #endif
 };
index 7957fef..6f2fe0f 100644 (file)
@@ -96,6 +96,8 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
                                            const char *name);
 int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
                        unsigned int *tlv, const char **slaves);
+void snd_hda_codec_reset(struct hda_codec *codec);
+int snd_hda_codec_configure(struct hda_codec *codec);
 
 /* amp value bits */
 #define HDA_AMP_MUTE   0x80
@@ -282,6 +284,12 @@ int snd_hda_codec_proc_new(struct hda_codec *codec);
 static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
 #endif
 
+#define SND_PRINT_RATES_ADVISED_BUFSIZE        80
+void snd_print_pcm_rates(int pcm, char *buf, int buflen);
+
+#define SND_PRINT_BITS_ADVISED_BUFSIZE 16
+void snd_print_pcm_bits(int pcm, char *buf, int buflen);
+
 /*
  * Misc
  */
@@ -364,17 +372,17 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 /* amp values */
 #define AMP_IN_MUTE(idx)       (0x7080 | ((idx)<<8))
 #define AMP_IN_UNMUTE(idx)     (0x7000 | ((idx)<<8))
-#define AMP_OUT_MUTE   0xb080
-#define AMP_OUT_UNMUTE 0xb000
-#define AMP_OUT_ZERO   0xb000
+#define AMP_OUT_MUTE           0xb080
+#define AMP_OUT_UNMUTE         0xb000
+#define AMP_OUT_ZERO           0xb000
 /* pinctl values */
 #define PIN_IN                 (AC_PINCTL_IN_EN)
-#define PIN_VREFHIZ    (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ)
+#define PIN_VREFHIZ            (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ)
 #define PIN_VREF50             (AC_PINCTL_IN_EN | AC_PINCTL_VREF_50)
-#define PIN_VREFGRD    (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD)
+#define PIN_VREFGRD            (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD)
 #define PIN_VREF80             (AC_PINCTL_IN_EN | AC_PINCTL_VREF_80)
-#define PIN_VREF100    (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100)
-#define PIN_OUT                (AC_PINCTL_OUT_EN)
+#define PIN_VREF100            (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100)
+#define PIN_OUT                        (AC_PINCTL_OUT_EN)
 #define PIN_HP                 (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
 #define PIN_HP_AMP             (AC_PINCTL_HP_EN)
 
@@ -393,10 +401,26 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
 int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
                              unsigned int caps);
 
+int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl);
+void snd_hda_ctls_clear(struct hda_codec *codec);
+
 /*
  * hwdep interface
  */
+#ifdef CONFIG_SND_HDA_HWDEP
 int snd_hda_create_hwdep(struct hda_codec *codec);
+#else
+static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; }
+#endif
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+int snd_hda_hwdep_add_sysfs(struct hda_codec *codec);
+#else
+static inline int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
+{
+       return 0;
+}
+#endif
 
 /*
  * power-management
@@ -430,4 +454,66 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
 #define get_amp_direction(kc)  (((kc)->private_value >> 18) & 0x1)
 #define get_amp_index(kc)      (((kc)->private_value >> 19) & 0xf)
 
+/*
+ * CEA Short Audio Descriptor data
+ */
+struct cea_sad {
+       int     channels;
+       int     format;         /* (format == 0) indicates invalid SAD */
+       int     rates;
+       int     sample_bits;    /* for LPCM */
+       int     max_bitrate;    /* for AC3...ATRAC */
+       int     profile;        /* for WMAPRO */
+};
+
+#define ELD_FIXED_BYTES        20
+#define ELD_MAX_MNL    16
+#define ELD_MAX_SAD    16
+
+/*
+ * ELD: EDID Like Data
+ */
+struct hdmi_eld {
+       int     eld_size;
+       int     baseline_len;
+       int     eld_ver;        /* (eld_ver == 0) indicates invalid ELD */
+       int     cea_edid_ver;
+       char    monitor_name[ELD_MAX_MNL + 1];
+       int     manufacture_id;
+       int     product_id;
+       u64     port_id;
+       int     support_hdcp;
+       int     support_ai;
+       int     conn_type;
+       int     aud_synch_delay;
+       int     spk_alloc;
+       int     sad_count;
+       struct cea_sad sad[ELD_MAX_SAD];
+#ifdef CONFIG_PROC_FS
+       struct snd_info_entry *proc_entry;
+#endif
+};
+
+int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid);
+int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t);
+void snd_hdmi_show_eld(struct hdmi_eld *eld);
+
+#ifdef CONFIG_PROC_FS
+int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld);
+void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld);
+#else
+static inline int snd_hda_eld_proc_new(struct hda_codec *codec,
+                                      struct hdmi_eld *eld)
+{
+       return 0;
+}
+static inline void snd_hda_eld_proc_free(struct hda_codec *codec,
+                                        struct hdmi_eld *eld)
+{
+}
+#endif
+
+#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
+void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen);
+
 #endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h
deleted file mode 100644 (file)
index dfbcfa8..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * HDA Patches - included by hda_codec.c
- */
-
-/* Realtek codecs */
-extern struct hda_codec_preset snd_hda_preset_realtek[];
-/* C-Media codecs */
-extern struct hda_codec_preset snd_hda_preset_cmedia[];
-/* Analog Devices codecs */
-extern struct hda_codec_preset snd_hda_preset_analog[];
-/* SigmaTel codecs */
-extern struct hda_codec_preset snd_hda_preset_sigmatel[];
-/* SiLabs 3054/3055 modem codecs */
-extern struct hda_codec_preset snd_hda_preset_si3054[];
-/* ATI HDMI codecs */
-extern struct hda_codec_preset snd_hda_preset_atihdmi[];
-/* Conexant audio codec */
-extern struct hda_codec_preset snd_hda_preset_conexant[];
-/* VIA codecs */
-extern struct hda_codec_preset snd_hda_preset_via[];
-/* NVIDIA HDMI codecs */
-extern struct hda_codec_preset snd_hda_preset_nvhdmi[];
index c39af98..7ca66d6 100644 (file)
@@ -91,31 +91,21 @@ static void print_amp_vals(struct snd_info_buffer *buffer,
 
 static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm)
 {
-       static unsigned int rates[] = {
-               8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
-               96000, 176400, 192000, 384000
-       };
-       int i;
+       char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
 
        pcm &= AC_SUPPCM_RATES;
        snd_iprintf(buffer, "    rates [0x%x]:", pcm);
-       for (i = 0; i < ARRAY_SIZE(rates); i++) 
-               if (pcm & (1 << i))
-                       snd_iprintf(buffer, " %d", rates[i]);
-       snd_iprintf(buffer, "\n");
+       snd_print_pcm_rates(pcm, buf, sizeof(buf));
+       snd_iprintf(buffer, "%s\n", buf);
 }
 
 static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm)
 {
-       static unsigned int bits[] = { 8, 16, 20, 24, 32 };
-       int i;
+       char buf[SND_PRINT_BITS_ADVISED_BUFSIZE];
 
-       pcm = (pcm >> 16) & 0xff;
-       snd_iprintf(buffer, "    bits [0x%x]:", pcm);
-       for (i = 0; i < ARRAY_SIZE(bits); i++)
-               if (pcm & (1 << i))
-                       snd_iprintf(buffer, " %d", bits[i]);
-       snd_iprintf(buffer, "\n");
+       snd_iprintf(buffer, "    bits [0x%x]:", (pcm >> 16) & 0xff);
+       snd_print_pcm_bits(pcm, buf, sizeof(buf));
+       snd_iprintf(buffer, "%s\n", buf);
 }
 
 static void print_pcm_formats(struct snd_info_buffer *buffer,
@@ -145,32 +135,6 @@ static void print_pcm_caps(struct snd_info_buffer *buffer,
        print_pcm_formats(buffer, stream);
 }
 
-static const char *get_jack_location(u32 cfg)
-{
-       static char *bases[7] = {
-               "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
-       };
-       static unsigned char specials_idx[] = {
-               0x07, 0x08,
-               0x17, 0x18, 0x19,
-               0x37, 0x38
-       };
-       static char *specials[] = {
-               "Rear Panel", "Drive Bar",
-               "Riser", "HDMI", "ATAPI",
-               "Mobile-In", "Mobile-Out"
-       };
-       int i;
-       cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
-       if ((cfg & 0x0f) < 7)
-               return bases[cfg & 0x0f];
-       for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
-               if (cfg == specials_idx[i])
-                       return specials[i];
-       }
-       return "UNKNOWN";
-}
-
 static const char *get_jack_connection(u32 cfg)
 {
        static char *names[16] = {
@@ -206,13 +170,6 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
                           int *supports_vref)
 {
        static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
-       static char *jack_types[16] = {
-               "Line Out", "Speaker", "HP Out", "CD",
-               "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
-               "Line In", "Aux", "Mic", "Telephony",
-               "SPDIF In", "Digitial In", "Reserved", "Other"
-       };
-       static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
        unsigned int caps, val;
 
        caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
@@ -274,9 +231,9 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
        caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
        snd_iprintf(buffer, "  Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
                    jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
-                   jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT],
-                   jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3],
-                   get_jack_location(caps));
+                   snd_hda_get_jack_type(caps),
+                   snd_hda_get_jack_connectivity(caps),
+                   snd_hda_get_jack_location(caps));
        snd_iprintf(buffer, "    Conn = %s, Color = %s\n",
                    get_jack_connection(caps),
                    get_jack_color(caps));
@@ -457,17 +414,6 @@ static void print_conn_list(struct snd_info_buffer *buffer,
        }
 }
 
-static void print_realtek_coef(struct snd_info_buffer *buffer,
-                              struct hda_codec *codec, hda_nid_t nid)
-{
-       int coeff = snd_hda_codec_read(codec, nid, 0,
-                                      AC_VERB_GET_PROC_COEF, 0);
-       snd_iprintf(buffer, "  Processing Coefficient: 0x%02x\n", coeff);
-       coeff = snd_hda_codec_read(codec, nid, 0,
-                                  AC_VERB_GET_COEF_INDEX, 0);
-       snd_iprintf(buffer, "  Coefficient Index: 0x%02x\n", coeff);
-}
-
 static void print_gpio(struct snd_info_buffer *buffer,
                       struct hda_codec *codec, hda_nid_t nid)
 {
@@ -500,12 +446,13 @@ static void print_gpio(struct snd_info_buffer *buffer,
        for (i = 0; i < max; ++i)
                snd_iprintf(buffer,
                            "  IO[%d]: enable=%d, dir=%d, wake=%d, "
-                           "sticky=%d, data=%d\n", i,
+                           "sticky=%d, data=%d, unsol=%d\n", i,
                            (enable & (1<<i)) ? 1 : 0,
                            (direction & (1<<i)) ? 1 : 0,
                            (wake & (1<<i)) ? 1 : 0,
                            (sticky & (1<<i)) ? 1 : 0,
-                           (data & (1<<i)) ? 1 : 0);
+                           (data & (1<<i)) ? 1 : 0,
+                           (unsol & (1<<i)) ? 1 : 0);
        /* FIXME: add GPO and GPI pin information */
 }
 
@@ -513,12 +460,11 @@ static void print_codec_info(struct snd_info_entry *entry,
                             struct snd_info_buffer *buffer)
 {
        struct hda_codec *codec = entry->private_data;
-       char buf[32];
        hda_nid_t nid;
        int i, nodes;
 
-       snd_hda_get_codec_name(codec, buf, sizeof(buf));
-       snd_iprintf(buffer, "Codec: %s\n", buf);
+       snd_iprintf(buffer, "Codec: %s\n",
+                   codec->name ? codec->name : "Not Set");
        snd_iprintf(buffer, "Address: %d\n", codec->addr);
        snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id);
        snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id);
@@ -547,6 +493,8 @@ static void print_codec_info(struct snd_info_entry *entry,
        }
 
        print_gpio(buffer, codec, codec->afg);
+       if (codec->proc_widget_hook)
+               codec->proc_widget_hook(buffer, codec, codec->afg);
 
        for (i = 0; i < nodes; i++, nid++) {
                unsigned int wid_caps =
@@ -649,9 +597,8 @@ static void print_codec_info(struct snd_info_entry *entry,
                if (wid_caps & AC_WCAP_PROC_WID)
                        print_proc_caps(buffer, codec, nid);
 
-               /* NID 0x20 == Realtek Define Registers */
-               if (codec->vendor_id == 0x10ec && nid == 0x20)
-                       print_realtek_coef(buffer, codec, nid);
+               if (codec->proc_widget_hook)
+                       codec->proc_widget_hook(buffer, codec, nid);
        }
        snd_hda_power_down(codec);
 }
index 686c774..26247cf 100644 (file)
@@ -27,7 +27,6 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 
 struct ad198x_spec {
        struct snd_kcontrol_new *mixers[5];
@@ -67,8 +66,7 @@ struct ad198x_spec {
 
        /* dynamic controls, init_verbs and input_mux */
        struct auto_pin_cfg autocfg;
-       unsigned int num_kctl_alloc, num_kctl_used;
-       struct snd_kcontrol_new *kctl_alloc;
+       struct snd_array kctls;
        struct hda_input_mux private_imux;
        hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
 
@@ -154,6 +152,8 @@ static const char *ad_slave_sws[] = {
        NULL
 };
 
+static void ad198x_free_kctls(struct hda_codec *codec);
+
 static int ad198x_build_controls(struct hda_codec *codec)
 {
        struct ad198x_spec *spec = codec->spec;
@@ -202,6 +202,7 @@ static int ad198x_build_controls(struct hda_codec *codec)
                        return err;
        }
 
+       ad198x_free_kctls(codec); /* no longer needed */
        return 0;
 }
 
@@ -375,16 +376,27 @@ static int ad198x_build_pcms(struct hda_codec *codec)
        return 0;
 }
 
-static void ad198x_free(struct hda_codec *codec)
+static void ad198x_free_kctls(struct hda_codec *codec)
 {
        struct ad198x_spec *spec = codec->spec;
-       unsigned int i;
 
-       if (spec->kctl_alloc) {
-               for (i = 0; i < spec->num_kctl_used; i++)
-                       kfree(spec->kctl_alloc[i].name);
-               kfree(spec->kctl_alloc);
+       if (spec->kctls.list) {
+               struct snd_kcontrol_new *kctl = spec->kctls.list;
+               int i;
+               for (i = 0; i < spec->kctls.used; i++)
+                       kfree(kctl[i].name);
        }
+       snd_array_free(&spec->kctls);
+}
+
+static void ad198x_free(struct hda_codec *codec)
+{
+       struct ad198x_spec *spec = codec->spec;
+
+       if (!spec)
+               return;
+
+       ad198x_free_kctls(codec);
        kfree(codec->spec);
 }
 
@@ -625,6 +637,36 @@ static struct hda_input_mux ad1986a_automic_capture_source = {
 };
 
 static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
+       HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
+       HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
+       HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Capture Source",
+               .info = ad198x_mux_enum_info,
+               .get = ad198x_mux_enum_get,
+               .put = ad198x_mux_enum_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "External Amplifier",
+               .info = ad198x_eapd_info,
+               .get = ad198x_eapd_get,
+               .put = ad198x_eapd_put,
+               .private_value = 0x1b | (1 << 8), /* port-D, inversed */
+       },
+       { } /* end */
+};
+
+static struct snd_kcontrol_new ad1986a_samsung_mixers[] = {
        HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
        HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
        HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
@@ -917,6 +959,7 @@ enum {
        AD1986A_LAPTOP_EAPD,
        AD1986A_LAPTOP_AUTOMUTE,
        AD1986A_ULTRA,
+       AD1986A_SAMSUNG,
        AD1986A_MODELS
 };
 
@@ -927,6 +970,7 @@ static const char *ad1986a_models[AD1986A_MODELS] = {
        [AD1986A_LAPTOP_EAPD]   = "laptop-eapd",
        [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
        [AD1986A_ULTRA]         = "ultra",
+       [AD1986A_SAMSUNG]       = "samsung",
 };
 
 static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
@@ -949,9 +993,9 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
        SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
-       SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD),
+       SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_SAMSUNG),
+       SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_SAMSUNG),
+       SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_SAMSUNG),
        SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
        SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
        SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
@@ -1033,6 +1077,17 @@ static int patch_ad1986a(struct hda_codec *codec)
                break;
        case AD1986A_LAPTOP_EAPD:
                spec->mixers[0] = ad1986a_laptop_eapd_mixers;
+               spec->num_init_verbs = 2;
+               spec->init_verbs[1] = ad1986a_eapd_init_verbs;
+               spec->multiout.max_channels = 2;
+               spec->multiout.num_dacs = 1;
+               spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
+               if (!is_jack_available(codec, 0x25))
+                       spec->multiout.dig_out_nid = 0;
+               spec->input_mux = &ad1986a_laptop_eapd_capture_source;
+               break;
+       case AD1986A_SAMSUNG:
+               spec->mixers[0] = ad1986a_samsung_mixers;
                spec->num_init_verbs = 3;
                spec->init_verbs[1] = ad1986a_eapd_init_verbs;
                spec->init_verbs[2] = ad1986a_automic_verbs;
@@ -2452,9 +2507,6 @@ static struct hda_amp_list ad1988_loopbacks[] = {
  * Automatic parse of I/O pins from the BIOS configuration
  */
 
-#define NUM_CONTROL_ALLOC      32
-#define NUM_VERB_ALLOC         32
-
 enum {
        AD_CTL_WIDGET_VOL,
        AD_CTL_WIDGET_MUTE,
@@ -2472,27 +2524,15 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name,
 {
        struct snd_kcontrol_new *knew;
 
-       if (spec->num_kctl_used >= spec->num_kctl_alloc) {
-               int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
-               knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
-               if (! knew)
-                       return -ENOMEM;
-               if (spec->kctl_alloc) {
-                       memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
-                       kfree(spec->kctl_alloc);
-               }
-               spec->kctl_alloc = knew;
-               spec->num_kctl_alloc = num;
-       }
-
-       knew = &spec->kctl_alloc[spec->num_kctl_used];
+       snd_array_init(&spec->kctls, sizeof(*knew), 32);
+       knew = snd_array_new(&spec->kctls);
+       if (!knew)
+               return -ENOMEM;
        *knew = ad1988_control_templates[type];
        knew->name = kstrdup(name, GFP_KERNEL);
        if (! knew->name)
                return -ENOMEM;
        knew->private_value = val;
-       spec->num_kctl_used++;
        return 0;
 }
 
@@ -2846,8 +2886,8 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
        if (spec->autocfg.dig_in_pin)
                spec->dig_in_nid = AD1988_SPDIF_IN;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
        spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
 
@@ -3861,6 +3901,7 @@ static const char *ad1884a_models[AD1884A_MODELS] = {
 static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
        SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
+       SND_PCI_QUIRK(0x103c, 0x30e6, "HP 6730b", AD1884A_LAPTOP),
        SND_PCI_QUIRK(0x103c, 0x30e7, "HP EliteBook 8530p", AD1884A_LAPTOP),
        SND_PCI_QUIRK(0x103c, 0x3614, "HP 6730s", AD1884A_LAPTOP),
        SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
@@ -4267,7 +4308,7 @@ static int patch_ad1882(struct hda_codec *codec)
 /*
  * patch entries
  */
-struct hda_codec_preset snd_hda_preset_analog[] = {
+static struct hda_codec_preset snd_hda_preset_analog[] = {
        { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
        { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
        { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
@@ -4285,3 +4326,26 @@ struct hda_codec_preset snd_hda_preset_analog[] = {
        { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
        {} /* terminator */
 };
+
+MODULE_ALIAS("snd-hda-codec-id:11d4*");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Analog Devices HD-audio codec");
+
+static struct hda_codec_preset_list analog_list = {
+       .preset = snd_hda_preset_analog,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_analog_init(void)
+{
+       return snd_hda_add_codec_preset(&analog_list);
+}
+
+static void __exit patch_analog_exit(void)
+{
+       snd_hda_delete_codec_preset(&analog_list);
+}
+
+module_init(patch_analog_init)
+module_exit(patch_analog_exit)
index ba61575..233e477 100644 (file)
@@ -27,7 +27,6 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 
 struct atihdmi_spec {
        struct hda_multi_out multiout;
@@ -187,13 +186,40 @@ static int patch_atihdmi(struct hda_codec *codec)
 /*
  * patch entries
  */
-struct hda_codec_preset snd_hda_preset_atihdmi[] = {
-       { .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
-       { .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
-       { .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
-       { .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi },
+static struct hda_codec_preset snd_hda_preset_atihdmi[] = {
+       { .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
+       { .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
+       { .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
+       { .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
        { .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi },
-       { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_atihdmi },
        { .id = 0x17e80047, .name = "Chrontel HDMI",  .patch = patch_atihdmi },
        {} /* terminator */
 };
+
+MODULE_ALIAS("snd-hda-codec-id:1002793c");
+MODULE_ALIAS("snd-hda-codec-id:10027919");
+MODULE_ALIAS("snd-hda-codec-id:1002791a");
+MODULE_ALIAS("snd-hda-codec-id:1002aa01");
+MODULE_ALIAS("snd-hda-codec-id:10951390");
+MODULE_ALIAS("snd-hda-codec-id:17e80047");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ATI HDMI HD-audio codec");
+
+static struct hda_codec_preset_list atihdmi_list = {
+       .preset = snd_hda_preset_atihdmi,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_atihdmi_init(void)
+{
+       return snd_hda_add_codec_preset(&atihdmi_list);
+}
+
+static void __exit patch_atihdmi_exit(void)
+{
+       snd_hda_delete_codec_preset(&atihdmi_list);
+}
+
+module_init(patch_atihdmi_init)
+module_exit(patch_atihdmi_exit)
index 6ef57fb..f3ebe83 100644 (file)
@@ -28,7 +28,6 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 #define NUM_PINS       11
 
 
@@ -736,8 +735,32 @@ static int patch_cmi9880(struct hda_codec *codec)
 /*
  * patch entries
  */
-struct hda_codec_preset snd_hda_preset_cmedia[] = {
+static struct hda_codec_preset snd_hda_preset_cmedia[] = {
        { .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
        { .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
        {} /* terminator */
 };
+
+MODULE_ALIAS("snd-hda-codec-id:13f69880");
+MODULE_ALIAS("snd-hda-codec-id:434d4980");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("C-Media HD-audio codec");
+
+static struct hda_codec_preset_list cmedia_list = {
+       .preset = snd_hda_preset_cmedia,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_cmedia_init(void)
+{
+       return snd_hda_add_codec_preset(&cmedia_list);
+}
+
+static void __exit patch_cmedia_exit(void)
+{
+       snd_hda_delete_codec_preset(&cmedia_list);
+}
+
+module_init(patch_cmedia_init)
+module_exit(patch_cmedia_exit)
index 7c1eb23..b20e1ce 100644 (file)
@@ -27,7 +27,6 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 
 #define CXT_PIN_DIR_IN              0x00
 #define CXT_PIN_DIR_OUT             0x01
@@ -86,8 +85,6 @@ struct conexant_spec {
 
        /* dynamic controls, init_verbs and input_mux */
        struct auto_pin_cfg autocfg;
-       unsigned int num_kctl_alloc, num_kctl_used;
-       struct snd_kcontrol_new *kctl_alloc;
        struct hda_input_mux private_imux;
        hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
 
@@ -344,15 +341,6 @@ static int conexant_init(struct hda_codec *codec)
 
 static void conexant_free(struct hda_codec *codec)
 {
-        struct conexant_spec *spec = codec->spec;
-        unsigned int i;
-
-        if (spec->kctl_alloc) {
-                for (i = 0; i < spec->num_kctl_used; i++)
-                        kfree(spec->kctl_alloc[i].name);
-                kfree(spec->kctl_alloc);
-        }
-
        kfree(codec->spec);
 }
 
@@ -1782,7 +1770,7 @@ static int patch_cxt5051(struct hda_codec *codec)
 /*
  */
 
-struct hda_codec_preset snd_hda_preset_conexant[] = {
+static struct hda_codec_preset snd_hda_preset_conexant[] = {
        { .id = 0x14f15045, .name = "CX20549 (Venice)",
          .patch = patch_cxt5045 },
        { .id = 0x14f15047, .name = "CX20551 (Waikiki)",
@@ -1791,3 +1779,28 @@ struct hda_codec_preset snd_hda_preset_conexant[] = {
          .patch = patch_cxt5051 },
        {} /* terminator */
 };
+
+MODULE_ALIAS("snd-hda-codec-id:14f15045");
+MODULE_ALIAS("snd-hda-codec-id:14f15047");
+MODULE_ALIAS("snd-hda-codec-id:14f15051");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Conexant HD-audio codec");
+
+static struct hda_codec_preset_list conexant_list = {
+       .preset = snd_hda_preset_conexant,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_conexant_init(void)
+{
+       return snd_hda_add_codec_preset(&conexant_list);
+}
+
+static void __exit patch_conexant_exit(void)
+{
+       snd_hda_delete_codec_preset(&conexant_list);
+}
+
+module_init(patch_conexant_init)
+module_exit(patch_conexant_exit)
diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c
new file mode 100644 (file)
index 0000000..3564f4e
--- /dev/null
@@ -0,0 +1,711 @@
+/*
+ *
+ *  patch_intelhdmi.c - Patch for Intel HDMI codecs
+ *
+ *  Copyright(c) 2008 Intel Corporation. All rights reserved.
+ *
+ *  Authors:
+ *                     Jiang Zhe <zhe.jiang@intel.com>
+ *                     Wu Fengguang <wfg@linux.intel.com>
+ *
+ *  Maintained by:
+ *                     Wu Fengguang <wfg@linux.intel.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the Free
+ *  Software Foundation; either version 2 of the License, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ *  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software Foundation,
+ *  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+#define CVT_NID                0x02    /* audio converter */
+#define PIN_NID                0x03    /* HDMI output pin */
+
+#define INTEL_HDMI_EVENT_TAG           0x08
+
+struct intel_hdmi_spec {
+       struct hda_multi_out multiout;
+       struct hda_pcm pcm_rec;
+       struct hdmi_eld sink_eld;
+};
+
+static struct hda_verb pinout_enable_verb[] = {
+       {PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {} /* terminator */
+};
+
+static struct hda_verb pinout_disable_verb[] = {
+       {PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00},
+       {}
+};
+
+static struct hda_verb unsolicited_response_verb[] = {
+       {PIN_NID, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN |
+                                                 INTEL_HDMI_EVENT_TAG},
+       {}
+};
+
+static struct hda_verb def_chan_map[] = {
+       {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x00},
+       {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x11},
+       {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x22},
+       {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x33},
+       {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x44},
+       {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x55},
+       {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x66},
+       {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x77},
+       {}
+};
+
+
+struct hdmi_audio_infoframe {
+       u8 type; /* 0x84 */
+       u8 ver;  /* 0x01 */
+       u8 len;  /* 0x0a */
+
+       u8 checksum;    /* PB0 */
+       u8 CC02_CT47;   /* CC in bits 0:2, CT in 4:7 */
+       u8 SS01_SF24;
+       u8 CXT04;
+       u8 CA;
+       u8 LFEPBL01_LSV36_DM_INH7;
+       u8 reserved[5]; /* PB6 - PB10 */
+};
+
+/*
+ * CEA speaker placement:
+ *
+ *        FLH       FCH        FRH
+ *  FLW    FL  FLC   FC   FRC   FR   FRW
+ *
+ *                                  LFE
+ *                     TC
+ *
+ *          RL  RLC   RC   RRC   RR
+ *
+ * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
+ * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
+ */
+enum cea_speaker_placement {
+       FL  = (1 <<  0),        /* Front Left           */
+       FC  = (1 <<  1),        /* Front Center         */
+       FR  = (1 <<  2),        /* Front Right          */
+       FLC = (1 <<  3),        /* Front Left Center    */
+       FRC = (1 <<  4),        /* Front Right Center   */
+       RL  = (1 <<  5),        /* Rear Left            */
+       RC  = (1 <<  6),        /* Rear Center          */
+       RR  = (1 <<  7),        /* Rear Right           */
+       RLC = (1 <<  8),        /* Rear Left Center     */
+       RRC = (1 <<  9),        /* Rear Right Center    */
+       LFE = (1 << 10),        /* Low Frequency Effect */
+       FLW = (1 << 11),        /* Front Left Wide      */
+       FRW = (1 << 12),        /* Front Right Wide     */
+       FLH = (1 << 13),        /* Front Left High      */
+       FCH = (1 << 14),        /* Front Center High    */
+       FRH = (1 << 15),        /* Front Right High     */
+       TC  = (1 << 16),        /* Top Center           */
+};
+
+/*
+ * ELD SA bits in the CEA Speaker Allocation data block
+ */
+static int eld_speaker_allocation_bits[] = {
+       [0] = FL | FR,
+       [1] = LFE,
+       [2] = FC,
+       [3] = RL | RR,
+       [4] = RC,
+       [5] = FLC | FRC,
+       [6] = RLC | RRC,
+       /* the following are not defined in ELD yet */
+       [7] = FLW | FRW,
+       [8] = FLH | FRH,
+       [9] = TC,
+       [10] = FCH,
+};
+
+struct cea_channel_speaker_allocation {
+       int ca_index;
+       int speakers[8];
+
+       /* derived values, just for convenience */
+       int channels;
+       int spk_mask;
+};
+
+/*
+ * This is an ordered list!
+ *
+ * The preceding ones have better chances to be selected by
+ * hdmi_setup_channel_allocation().
+ */
+static struct cea_channel_speaker_allocation channel_allocations[] = {
+/*                       channel:   8     7    6    5    4     3    2    1  */
+{ .ca_index = 0x00,  .speakers = {   0,    0,   0,   0,   0,    0,  FR,  FL } },
+                                /* 2.1 */
+{ .ca_index = 0x01,  .speakers = {   0,    0,   0,   0,   0,  LFE,  FR,  FL } },
+                                /* Dolby Surround */
+{ .ca_index = 0x02,  .speakers = {   0,    0,   0,   0,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x03,  .speakers = {   0,    0,   0,   0,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x04,  .speakers = {   0,    0,   0,  RC,   0,    0,  FR,  FL } },
+{ .ca_index = 0x05,  .speakers = {   0,    0,   0,  RC,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x06,  .speakers = {   0,    0,   0,  RC,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x07,  .speakers = {   0,    0,   0,  RC,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x08,  .speakers = {   0,    0,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x09,  .speakers = {   0,    0,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x0a,  .speakers = {   0,    0,  RR,  RL,  FC,    0,  FR,  FL } },
+                                /* 5.1 */
+{ .ca_index = 0x0b,  .speakers = {   0,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x0c,  .speakers = {   0,   RC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x0d,  .speakers = {   0,   RC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x0e,  .speakers = {   0,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+                                /* 6.1 */
+{ .ca_index = 0x0f,  .speakers = {   0,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x10,  .speakers = { RRC,  RLC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x11,  .speakers = { RRC,  RLC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x12,  .speakers = { RRC,  RLC,  RR,  RL,  FC,    0,  FR,  FL } },
+                                /* 7.1 */
+{ .ca_index = 0x13,  .speakers = { RRC,  RLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x14,  .speakers = { FRC,  FLC,   0,   0,   0,    0,  FR,  FL } },
+{ .ca_index = 0x15,  .speakers = { FRC,  FLC,   0,   0,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x16,  .speakers = { FRC,  FLC,   0,   0,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x17,  .speakers = { FRC,  FLC,   0,   0,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x18,  .speakers = { FRC,  FLC,   0,  RC,   0,    0,  FR,  FL } },
+{ .ca_index = 0x19,  .speakers = { FRC,  FLC,   0,  RC,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x1a,  .speakers = { FRC,  FLC,   0,  RC,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x1b,  .speakers = { FRC,  FLC,   0,  RC,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x1c,  .speakers = { FRC,  FLC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x1d,  .speakers = { FRC,  FLC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x1e,  .speakers = { FRC,  FLC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x1f,  .speakers = { FRC,  FLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x20,  .speakers = {   0,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x21,  .speakers = {   0,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x22,  .speakers = {  TC,    0,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x23,  .speakers = {  TC,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x24,  .speakers = { FRH,  FLH,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x25,  .speakers = { FRH,  FLH,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x26,  .speakers = { FRW,  FLW,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x27,  .speakers = { FRW,  FLW,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x28,  .speakers = {  TC,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x29,  .speakers = {  TC,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2a,  .speakers = { FCH,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2b,  .speakers = { FCH,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2c,  .speakers = {  TC,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2d,  .speakers = {  TC,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2e,  .speakers = { FRH,  FLH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2f,  .speakers = { FRH,  FLH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x30,  .speakers = { FRW,  FLW,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x31,  .speakers = { FRW,  FLW,  RR,  RL,  FC,  LFE,  FR,  FL } },
+};
+
+/*
+ * HDMI routines
+ */
+
+#ifdef BE_PARANOID
+static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t nid,
+                               int *packet_index, int *byte_index)
+{
+       int val;
+
+       val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_INDEX, 0);
+
+       *packet_index = val >> 5;
+       *byte_index = val & 0x1f;
+}
+#endif
+
+static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t nid,
+                               int packet_index, int byte_index)
+{
+       int val;
+
+       val = (packet_index << 5) | (byte_index & 0x1f);
+
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
+}
+
+static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid,
+                               unsigned char val)
+{
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
+}
+
+static void hdmi_enable_output(struct hda_codec *codec)
+{
+       /* Enable Audio InfoFrame Transmission */
+       hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
+       snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+                                               AC_DIPXMIT_BEST);
+       /* Unmute */
+       if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
+               snd_hda_codec_write(codec, PIN_NID, 0,
+                               AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+       /* Enable pin out */
+       snd_hda_sequence_write(codec, pinout_enable_verb);
+}
+
+static void hdmi_disable_output(struct hda_codec *codec)
+{
+       snd_hda_sequence_write(codec, pinout_disable_verb);
+       if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
+               snd_hda_codec_write(codec, PIN_NID, 0,
+                               AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+       /*
+        * FIXME: noises may arise when playing music after reloading the
+        * kernel module, until the next X restart or monitor repower.
+        */
+}
+
+static int hdmi_get_channel_count(struct hda_codec *codec)
+{
+       return 1 + snd_hda_codec_read(codec, CVT_NID, 0,
+                                       AC_VERB_GET_CVT_CHAN_COUNT, 0);
+}
+
+static void hdmi_set_channel_count(struct hda_codec *codec, int chs)
+{
+       snd_hda_codec_write(codec, CVT_NID, 0,
+                                       AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
+
+       if (chs != hdmi_get_channel_count(codec))
+               snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n",
+                                       chs, hdmi_get_channel_count(codec));
+}
+
+static void hdmi_debug_channel_mapping(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       int i;
+       int slot;
+
+       for (i = 0; i < 8; i++) {
+               slot = snd_hda_codec_read(codec, CVT_NID, 0,
+                                               AC_VERB_GET_HDMI_CHAN_SLOT, i);
+               printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
+                                               slot >> 4, slot & 0x7);
+       }
+#endif
+}
+
+static void hdmi_parse_eld(struct hda_codec *codec)
+{
+       struct intel_hdmi_spec *spec = codec->spec;
+       struct hdmi_eld *eld = &spec->sink_eld;
+
+       if (!snd_hdmi_get_eld(eld, codec, PIN_NID))
+               snd_hdmi_show_eld(eld);
+}
+
+
+/*
+ * Audio InfoFrame routines
+ */
+
+static void hdmi_debug_dip_size(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       int i;
+       int size;
+
+       size = snd_hdmi_get_eld_size(codec, PIN_NID);
+       printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
+
+       for (i = 0; i < 8; i++) {
+               size = snd_hda_codec_read(codec, PIN_NID, 0,
+                                               AC_VERB_GET_HDMI_DIP_SIZE, i);
+               printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
+       }
+#endif
+}
+
+static void hdmi_clear_dip_buffers(struct hda_codec *codec)
+{
+#ifdef BE_PARANOID
+       int i, j;
+       int size;
+       int pi, bi;
+       for (i = 0; i < 8; i++) {
+               size = snd_hda_codec_read(codec, PIN_NID, 0,
+                                               AC_VERB_GET_HDMI_DIP_SIZE, i);
+               if (size == 0)
+                       continue;
+
+               hdmi_set_dip_index(codec, PIN_NID, i, 0x0);
+               for (j = 1; j < 1000; j++) {
+                       hdmi_write_dip_byte(codec, PIN_NID, 0x0);
+                       hdmi_get_dip_index(codec, PIN_NID, &pi, &bi);
+                       if (pi != i)
+                               snd_printd(KERN_INFO "dip index %d: %d != %d\n",
+                                               bi, pi, i);
+                       if (bi == 0) /* byte index wrapped around */
+                               break;
+               }
+               snd_printd(KERN_INFO
+                       "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
+                       i, size, j);
+       }
+#endif
+}
+
+static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
+                                       struct hdmi_audio_infoframe *ai)
+{
+       u8 *params = (u8 *)ai;
+       int i;
+
+       hdmi_debug_dip_size(codec);
+       hdmi_clear_dip_buffers(codec); /* be paranoid */
+
+       hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
+       for (i = 0; i < sizeof(ai); i++)
+               hdmi_write_dip_byte(codec, PIN_NID, params[i]);
+}
+
+/*
+ * Compute derived values in channel_allocations[].
+ */
+static void init_channel_allocations(void)
+{
+       int i, j;
+       struct cea_channel_speaker_allocation *p;
+
+       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+               p = channel_allocations + i;
+               p->channels = 0;
+               p->spk_mask = 0;
+               for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
+                       if (p->speakers[j]) {
+                               p->channels++;
+                               p->spk_mask |= p->speakers[j];
+                       }
+       }
+}
+
+/*
+ * The transformation takes two steps:
+ *
+ *     eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
+ *           spk_mask => (channel_allocations[])         => ai->CA
+ *
+ * TODO: it could select the wrong CA from multiple candidates.
+*/
+static int hdmi_setup_channel_allocation(struct hda_codec *codec,
+                                        struct hdmi_audio_infoframe *ai)
+{
+       struct intel_hdmi_spec *spec = codec->spec;
+       struct hdmi_eld *eld = &spec->sink_eld;
+       int i;
+       int spk_mask = 0;
+       int channels = 1 + (ai->CC02_CT47 & 0x7);
+       char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+
+       /*
+        * CA defaults to 0 for basic stereo audio
+        */
+       if (!eld->eld_ver)
+               return 0;
+       if (!eld->spk_alloc)
+               return 0;
+       if (channels <= 2)
+               return 0;
+
+       /*
+        * expand ELD's speaker allocation mask
+        *
+        * ELD tells the speaker mask in a compact(paired) form,
+        * expand ELD's notions to match the ones used by Audio InfoFrame.
+        */
+       for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+               if (eld->spk_alloc & (1 << i))
+                       spk_mask |= eld_speaker_allocation_bits[i];
+       }
+
+       /* search for the first working match in the CA table */
+       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+               if (channels == channel_allocations[i].channels &&
+                   (spk_mask & channel_allocations[i].spk_mask) ==
+                               channel_allocations[i].spk_mask) {
+                       ai->CA = channel_allocations[i].ca_index;
+                       break;
+               }
+       }
+
+       snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
+       snd_printdd(KERN_INFO
+                       "HDMI: select CA 0x%x for %d-channel allocation: %s\n",
+                       ai->CA, channels, buf);
+
+       return ai->CA;
+}
+
+static void hdmi_setup_channel_mapping(struct hda_codec *codec,
+                                       struct hdmi_audio_infoframe *ai)
+{
+       if (!ai->CA)
+               return;
+
+       /*
+        * TODO: adjust channel mapping if necessary
+        * ALSA sequence is front/surr/clfe/side?
+        */
+
+       snd_hda_sequence_write(codec, def_chan_map);
+       hdmi_debug_channel_mapping(codec);
+}
+
+
+static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
+                                       struct snd_pcm_substream *substream)
+{
+       struct hdmi_audio_infoframe ai = {
+               .type           = 0x84,
+               .ver            = 0x01,
+               .len            = 0x0a,
+               .CC02_CT47      = substream->runtime->channels - 1,
+       };
+
+       hdmi_setup_channel_allocation(codec, &ai);
+       hdmi_setup_channel_mapping(codec, &ai);
+
+       hdmi_fill_audio_infoframe(codec, &ai);
+}
+
+
+/*
+ * Unsolicited events
+ */
+
+static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
+{
+       int pind = !!(res & AC_UNSOL_RES_PD);
+       int eldv = !!(res & AC_UNSOL_RES_ELDV);
+
+       printk(KERN_INFO
+               "HDMI hot plug event: Presence_Detect=%d ELD_Valid=%d\n",
+               pind, eldv);
+
+       if (pind && eldv) {
+               hdmi_parse_eld(codec);
+               /* TODO: do real things about ELD */
+       }
+}
+
+static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
+{
+       int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+       int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
+       int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
+
+       printk(KERN_INFO
+               "HDMI content protection event: SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+               subtag,
+               cp_state,
+               cp_ready);
+
+       /* TODO */
+       if (cp_state)
+               ;
+       if (cp_ready)
+               ;
+}
+
+
+static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+       int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
+       int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+
+       if (tag != INTEL_HDMI_EVENT_TAG) {
+               snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
+               return;
+       }
+
+       if (subtag == 0)
+               hdmi_intrinsic_event(codec, res);
+       else
+               hdmi_non_intrinsic_event(codec, res);
+}
+
+/*
+ * Callbacks
+ */
+
+static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       struct snd_pcm_substream *substream)
+{
+       struct intel_hdmi_spec *spec = codec->spec;
+
+       return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo,
+                                        struct hda_codec *codec,
+                                        struct snd_pcm_substream *substream)
+{
+       struct intel_hdmi_spec *spec = codec->spec;
+
+       hdmi_disable_output(codec);
+
+       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                          struct hda_codec *codec,
+                                          unsigned int stream_tag,
+                                          unsigned int format,
+                                          struct snd_pcm_substream *substream)
+{
+       struct intel_hdmi_spec *spec = codec->spec;
+
+       snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+                                            format, substream);
+
+       hdmi_set_channel_count(codec, substream->runtime->channels);
+
+       hdmi_setup_audio_infoframe(codec, substream);
+
+       hdmi_enable_output(codec);
+
+       return 0;
+}
+
+static struct hda_pcm_stream intel_hdmi_pcm_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 8,
+       .nid = CVT_NID, /* NID to query formats and rates and setup streams */
+       .ops = {
+               .open    = intel_hdmi_playback_pcm_open,
+               .close   = intel_hdmi_playback_pcm_close,
+               .prepare = intel_hdmi_playback_pcm_prepare
+       },
+};
+
+static int intel_hdmi_build_pcms(struct hda_codec *codec)
+{
+       struct intel_hdmi_spec *spec = codec->spec;
+       struct hda_pcm *info = &spec->pcm_rec;
+
+       codec->num_pcms = 1;
+       codec->pcm_info = info;
+
+       info->name = "INTEL HDMI";
+       info->pcm_type = HDA_PCM_TYPE_HDMI;
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback;
+
+       return 0;
+}
+
+static int intel_hdmi_build_controls(struct hda_codec *codec)
+{
+       struct intel_hdmi_spec *spec = codec->spec;
+       int err;
+
+       err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static int intel_hdmi_init(struct hda_codec *codec)
+{
+       /* disable audio output as early as possible */
+       hdmi_disable_output(codec);
+
+       snd_hda_sequence_write(codec, unsolicited_response_verb);
+
+       return 0;
+}
+
+static void intel_hdmi_free(struct hda_codec *codec)
+{
+       struct intel_hdmi_spec *spec = codec->spec;
+
+       snd_hda_eld_proc_free(codec, &spec->sink_eld);
+       kfree(spec);
+}
+
+static struct hda_codec_ops intel_hdmi_patch_ops = {
+       .init                   = intel_hdmi_init,
+       .free                   = intel_hdmi_free,
+       .build_pcms             = intel_hdmi_build_pcms,
+       .build_controls         = intel_hdmi_build_controls,
+       .unsol_event            = intel_hdmi_unsol_event,
+};
+
+static int patch_intel_hdmi(struct hda_codec *codec)
+{
+       struct intel_hdmi_spec *spec;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       spec->multiout.num_dacs = 0;      /* no analog */
+       spec->multiout.max_channels = 8;
+       spec->multiout.dig_out_nid = CVT_NID;
+
+       codec->spec = spec;
+       codec->patch_ops = intel_hdmi_patch_ops;
+
+       snd_hda_eld_proc_new(codec, &spec->sink_eld);
+
+       init_channel_allocations();
+
+       return 0;
+}
+
+static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
+       { .id = 0x808629fb, .name = "G45 DEVCL",  .patch = patch_intel_hdmi },
+       { .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi },
+       { .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi },
+       { .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi },
+       { .id = 0x10951392, .name = "SiI1392 HDMI",     .patch = patch_intel_hdmi },
+       {} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:808629fb");
+MODULE_ALIAS("snd-hda-codec-id:80862801");
+MODULE_ALIAS("snd-hda-codec-id:80862802");
+MODULE_ALIAS("snd-hda-codec-id:80862803");
+MODULE_ALIAS("snd-hda-codec-id:10951392");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Intel HDMI HD-audio codec");
+
+static struct hda_codec_preset_list intel_list = {
+       .preset = snd_hda_preset_intelhdmi,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_intelhdmi_init(void)
+{
+       return snd_hda_add_codec_preset(&intel_list);
+}
+
+static void __exit patch_intelhdmi_exit(void)
+{
+       snd_hda_delete_codec_preset(&intel_list);
+}
+
+module_init(patch_intelhdmi_init)
+module_exit(patch_intelhdmi_exit)
index 2eed2c8..0270fda 100644 (file)
@@ -158,8 +158,34 @@ static int patch_nvhdmi(struct hda_codec *codec)
 /*
  * patch entries
  */
-struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
-       { .id = 0x10de0002, .name = "NVIDIA MCP78 HDMI", .patch = patch_nvhdmi },
-       { .id = 0x10de0007, .name = "NVIDIA MCP7A HDMI", .patch = patch_nvhdmi },
+static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
+       { .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi },
+       { .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi },
+       { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi },
        {} /* terminator */
 };
+
+MODULE_ALIAS("snd-hda-codec-id:10de0002");
+MODULE_ALIAS("snd-hda-codec-id:10de0007");
+MODULE_ALIAS("snd-hda-codec-id:10de0067");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Nvidia HDMI HD-audio codec");
+
+static struct hda_codec_preset_list nvhdmi_list = {
+       .preset = snd_hda_preset_nvhdmi,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_nvhdmi_init(void)
+{
+       return snd_hda_add_codec_preset(&nvhdmi_list);
+}
+
+static void __exit patch_nvhdmi_exit(void)
+{
+       snd_hda_delete_codec_preset(&nvhdmi_list);
+}
+
+module_init(patch_nvhdmi_init)
+module_exit(patch_nvhdmi_exit)
index a378c01..0bd4e6b 100644 (file)
@@ -30,7 +30,6 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 
 #define ALC880_FRONT_EVENT             0x01
 #define ALC880_DCVOL_EVENT             0x02
@@ -114,6 +113,7 @@ enum {
        ALC268_3ST,
        ALC268_TOSHIBA,
        ALC268_ACER,
+       ALC268_ACER_DMIC,
        ALC268_ACER_ASPIRE_ONE,
        ALC268_DELL,
        ALC268_ZEPTO,
@@ -130,6 +130,8 @@ enum {
        ALC269_QUANTA_FL1,
        ALC269_ASUS_EEEPC_P703,
        ALC269_ASUS_EEEPC_P901,
+       ALC269_FUJITSU,
+       ALC269_LIFEBOOK,
        ALC269_AUTO,
        ALC269_MODEL_LAST /* last tag */
 };
@@ -152,6 +154,7 @@ enum {
 enum {
        ALC660VD_3ST,
        ALC660VD_3ST_DIG,
+       ALC660VD_ASUS_V1S,
        ALC861VD_3ST,
        ALC861VD_3ST_DIG,
        ALC861VD_6ST_DIG,
@@ -212,6 +215,7 @@ enum {
        ALC883_TARGA_2ch_DIG,
        ALC883_ACER,
        ALC883_ACER_ASPIRE,
+       ALC888_ACER_ASPIRE_4930G,
        ALC883_MEDION,
        ALC883_MEDION_MD2,
        ALC883_LAPTOP_EAPD,
@@ -225,9 +229,11 @@ enum {
        ALC883_MITAC,
        ALC883_CLEVO_M720,
        ALC883_FUJITSU_PI2515,
+       ALC888_FUJITSU_XA3530,
        ALC883_3ST_6ch_INTEL,
        ALC888_ASUS_M90V,
        ALC888_ASUS_EEE1601,
+       ALC1200_ASUS_P5Q,
        ALC883_AUTO,
        ALC883_MODEL_LAST,
 };
@@ -239,6 +245,7 @@ struct alc_spec {
        /* codec parameterization */
        struct snd_kcontrol_new *mixers[5];     /* mixer arrays */
        unsigned int num_mixers;
+       struct snd_kcontrol_new *cap_mixer;     /* capture mixer */
 
        const struct hda_verb *init_verbs[5];   /* initialization verbs
                                                 * don't forget NULL
@@ -268,6 +275,7 @@ struct alc_spec {
        hda_nid_t *adc_nids;
        hda_nid_t *capsrc_nids;
        hda_nid_t dig_in_nid;           /* digital-in NID; optional */
+       unsigned char is_mix_capture;   /* matrix-style capture (non-mux) */
 
        /* capture source */
        unsigned int num_mux_defs;
@@ -284,8 +292,7 @@ struct alc_spec {
 
        /* dynamic controls, init_verbs and input_mux */
        struct auto_pin_cfg autocfg;
-       unsigned int num_kctl_alloc, num_kctl_used;
-       struct snd_kcontrol_new *kctl_alloc;
+       struct snd_array kctls;
        struct hda_input_mux private_imux;
        hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
 
@@ -323,6 +330,7 @@ struct alc_config_preset {
        struct snd_kcontrol_new *mixers[5]; /* should be identical size
                                             * with spec
                                             */
+       struct snd_kcontrol_new *cap_mixer; /* capture mixer */
        const struct hda_verb *init_verbs[5];
        unsigned int num_dacs;
        hda_nid_t *dac_nids;
@@ -375,14 +383,39 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct alc_spec *spec = codec->spec;
+       const struct hda_input_mux *imux;
        unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-       unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
+       unsigned int mux_idx;
        hda_nid_t nid = spec->capsrc_nids ?
                spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx];
-       return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol,
-                                    nid, &spec->cur_mux[adc_idx]);
-}
 
+       mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
+       imux = &spec->input_mux[mux_idx];
+
+       if (spec->is_mix_capture) {
+               /* Matrix-mixer style (e.g. ALC882) */
+               unsigned int *cur_val = &spec->cur_mux[adc_idx];
+               unsigned int i, idx;
+
+               idx = ucontrol->value.enumerated.item[0];
+               if (idx >= imux->num_items)
+                       idx = imux->num_items - 1;
+               if (*cur_val == idx)
+                       return 0;
+               for (i = 0; i < imux->num_items; i++) {
+                       unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
+                       snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
+                                                imux->items[i].index,
+                                                HDA_AMP_MUTE, v);
+               }
+               *cur_val = idx;
+               return 1;
+       } else {
+               /* MUX style (e.g. ALC880) */
+               return snd_hda_input_mux_put(codec, imux, ucontrol, nid,
+                                            &spec->cur_mux[adc_idx]);
+       }
+}
 
 /*
  * channel mode setting
@@ -716,6 +749,43 @@ static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol,
          .private_value = nid | (mask<<16) }
 #endif   /* CONFIG_SND_DEBUG */
 
+/*
+ */
+static void add_mixer(struct alc_spec *spec, struct snd_kcontrol_new *mix)
+{
+       if (snd_BUG_ON(spec->num_mixers >= ARRAY_SIZE(spec->mixers)))
+               return;
+       spec->mixers[spec->num_mixers++] = mix;
+}
+
+static void add_verb(struct alc_spec *spec, const struct hda_verb *verb)
+{
+       if (snd_BUG_ON(spec->num_init_verbs >= ARRAY_SIZE(spec->init_verbs)))
+               return;
+       spec->init_verbs[spec->num_init_verbs++] = verb;
+}
+
+#ifdef CONFIG_PROC_FS
+/*
+ * hook for proc
+ */
+static void print_realtek_coef(struct snd_info_buffer *buffer,
+                              struct hda_codec *codec, hda_nid_t nid)
+{
+       int coeff;
+
+       if (nid != 0x20)
+               return;
+       coeff = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0);
+       snd_iprintf(buffer, "  Processing Coefficient: 0x%02x\n", coeff);
+       coeff = snd_hda_codec_read(codec, nid, 0,
+                                  AC_VERB_GET_COEF_INDEX, 0);
+       snd_iprintf(buffer, "  Coefficient Index: 0x%02x\n", coeff);
+}
+#else
+#define print_realtek_coef     NULL
+#endif
+
 /*
  * set up from the preset table
  */
@@ -725,11 +795,11 @@ static void setup_preset(struct alc_spec *spec,
        int i;
 
        for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
-               spec->mixers[spec->num_mixers++] = preset->mixers[i];
+               add_mixer(spec, preset->mixers[i]);
+       spec->cap_mixer = preset->cap_mixer;
        for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i];
             i++)
-               spec->init_verbs[spec->num_init_verbs++] =
-                       preset->init_verbs[i];
+               add_verb(spec, preset->init_verbs[i]);
 
        spec->channel_mode = preset->channel_mode;
        spec->num_channel_mode = preset->num_channel_mode;
@@ -1106,6 +1176,226 @@ static void alc_fix_pincfg(struct hda_codec *codec,
        }
 }
 
+/*
+ * ALC888
+ */
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc888_4ST_ch2_intel_init[] = {
+/* Mic-in jack as mic in */
+       { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+       { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+/* Line-in jack as Line in */
+       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+/* Line-Out as Front */
+       { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00},
+       { } /* end */
+};
+
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc888_4ST_ch4_intel_init[] = {
+/* Mic-in jack as mic in */
+       { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+       { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+/* Line-in jack as Surround */
+       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+/* Line-Out as Front */
+       { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00},
+       { } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc888_4ST_ch6_intel_init[] = {
+/* Mic-in jack as CLFE */
+       { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+/* Line-in jack as Surround */
+       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+/* Line-Out as CLFE (workaround because Mic-in is not loud enough) */
+       { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+       { } /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc888_4ST_ch8_intel_init[] = {
+/* Mic-in jack as CLFE */
+       { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+/* Line-in jack as Surround */
+       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+/* Line-Out as Side */
+       { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+       { } /* end */
+};
+
+static struct hda_channel_mode alc888_4ST_8ch_intel_modes[4] = {
+       { 2, alc888_4ST_ch2_intel_init },
+       { 4, alc888_4ST_ch4_intel_init },
+       { 6, alc888_4ST_ch6_intel_init },
+       { 8, alc888_4ST_ch8_intel_init },
+};
+
+/*
+ * ALC888 Fujitsu Siemens Amillo xa3530
+ */
+
+static struct hda_verb alc888_fujitsu_xa3530_verbs[] = {
+/* Front Mic: set to PIN_IN (empty by default) */
+       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+/* Connect Internal HP to Front */
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+/* Connect Bass HP to Front */
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+/* Connect Line-Out side jack (SPDIF) to Side */
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+/* Connect Mic jack to CLFE */
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x18, AC_VERB_SET_CONNECT_SEL, 0x02},
+/* Connect Line-in jack to Surround */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
+/* Connect HP out jack to Front */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+/* Enable unsolicited event for HP jack and Line-out jack */
+       {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+       {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+       {}
+};
+
+static void alc888_fujitsu_xa3530_automute(struct hda_codec *codec)
+{
+       unsigned int present;
+       unsigned int bits;
+       /* Line out presence */
+       present = snd_hda_codec_read(codec, 0x17, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       /* HP out presence */
+       present = present || snd_hda_codec_read(codec, 0x1b, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       bits = present ? HDA_AMP_MUTE : 0;
+       /* Toggle internal speakers muting */
+       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+                                HDA_AMP_MUTE, bits);
+       /* Toggle internal bass muting */
+       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
+                                HDA_AMP_MUTE, bits);
+}
+
+static void alc888_fujitsu_xa3530_unsol_event(struct hda_codec *codec,
+               unsigned int res)
+{
+       if (res >> 26 == ALC880_HP_EVENT)
+               alc888_fujitsu_xa3530_automute(codec);
+}
+
+
+/*
+ * ALC888 Acer Aspire 4930G model
+ */
+
+static struct hda_verb alc888_acer_aspire_4930g_verbs[] = {
+/* Front Mic: set to PIN_IN (empty by default) */
+       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+/* Unselect Front Mic by default in input mixer 3 */
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)},
+/* Enable unsolicited event for HP jack */
+       {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+/* Connect Internal HP to front */
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+/* Connect HP out to front */
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+       { }
+};
+
+static struct hda_input_mux alc888_2_capture_sources[2] = {
+       /* Front mic only available on one ADC */
+       {
+               .num_items = 4,
+               .items = {
+                       { "Mic", 0x0 },
+                       { "Line", 0x2 },
+                       { "CD", 0x4 },
+                       { "Front Mic", 0xb },
+               },
+       },
+       {
+               .num_items = 3,
+               .items = {
+                       { "Mic", 0x0 },
+                       { "Line", 0x2 },
+                       { "CD", 0x4 },
+               },
+       }
+};
+
+static struct snd_kcontrol_new alc888_base_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
+               HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+       HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+       HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+       { } /* end */
+};
+
+static void alc888_acer_aspire_4930g_automute(struct hda_codec *codec)
+{
+       unsigned int present;
+       unsigned int bits;
+       present = snd_hda_codec_read(codec, 0x15, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       bits = present ? HDA_AMP_MUTE : 0;
+       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+                                HDA_AMP_MUTE, bits);
+}
+
+static void alc888_acer_aspire_4930g_unsol_event(struct hda_codec *codec,
+               unsigned int res)
+{
+       if (res >> 26 == ALC880_HP_EVENT)
+               alc888_acer_aspire_4930g_automute(codec);
+}
+
 /*
  * ALC880 3-stack model
  *
@@ -1205,49 +1495,126 @@ static struct snd_kcontrol_new alc880_three_stack_mixer[] = {
 };
 
 /* capture mixer elements */
-static struct snd_kcontrol_new alc880_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 3,
-               .info = alc_mux_enum_info,
-               .get = alc_mux_enum_get,
-               .put = alc_mux_enum_put,
-       },
-       { } /* end */
-};
+static int alc_cap_vol_info(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_info *uinfo)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       int err;
 
-/* capture mixer elements (in case NID 0x07 not available) */
-static struct snd_kcontrol_new alc880_capture_alt_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = alc_mux_enum_info,
-               .get = alc_mux_enum_get,
-               .put = alc_mux_enum_put,
-       },
-       { } /* end */
-};
+       mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+       kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0,
+                                                     HDA_INPUT);
+       err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+       mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */
+       return err;
+}
+
+static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+                          unsigned int size, unsigned int __user *tlv)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       int err;
+
+       mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+       kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0,
+                                                     HDA_INPUT);
+       err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+       mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */
+       return err;
+}
+
+typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol);
+
+static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol,
+                                getput_call_t func)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+       int err;
+
+       mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+       kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[adc_idx],
+                                                     3, 0, HDA_INPUT);
+       err = func(kcontrol, ucontrol);
+       mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */
+       return err;
+}
 
+static int alc_cap_vol_get(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol)
+{
+       return alc_cap_getput_caller(kcontrol, ucontrol,
+                                    snd_hda_mixer_amp_volume_get);
+}
+
+static int alc_cap_vol_put(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol)
+{
+       return alc_cap_getput_caller(kcontrol, ucontrol,
+                                    snd_hda_mixer_amp_volume_put);
+}
+
+/* capture mixer elements */
+#define alc_cap_sw_info                snd_ctl_boolean_stereo_info
+
+static int alc_cap_sw_get(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       return alc_cap_getput_caller(kcontrol, ucontrol,
+                                    snd_hda_mixer_amp_switch_get);
+}
+
+static int alc_cap_sw_put(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       return alc_cap_getput_caller(kcontrol, ucontrol,
+                                    snd_hda_mixer_amp_switch_put);
+}
+
+#define DEFINE_CAPMIX(num) \
+static struct snd_kcontrol_new alc_capture_mixer ## num[] = { \
+       { \
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+               .name = "Capture Switch", \
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+               .count = num, \
+               .info = alc_cap_sw_info, \
+               .get = alc_cap_sw_get, \
+               .put = alc_cap_sw_put, \
+       }, \
+       { \
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+               .name = "Capture Volume", \
+               .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+                          SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+                          SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), \
+               .count = num, \
+               .info = alc_cap_vol_info, \
+               .get = alc_cap_vol_get, \
+               .put = alc_cap_vol_put, \
+               .tlv = { .c = alc_cap_vol_tlv }, \
+       }, \
+       { \
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+               /* .name = "Capture Source", */ \
+               .name = "Input Source", \
+               .count = num, \
+               .info = alc_mux_enum_info, \
+               .get = alc_mux_enum_get, \
+               .put = alc_mux_enum_put, \
+       }, \
+       { } /* end */ \
+}
+
+/* up to three ADCs */
+DEFINE_CAPMIX(1);
+DEFINE_CAPMIX(2);
+DEFINE_CAPMIX(3);
 
 
 /*
@@ -1533,18 +1900,6 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
        HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = alc_mux_enum_info,
-               .get = alc_mux_enum_get,
-               .put = alc_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -1619,6 +1974,7 @@ static const char *alc_slave_vols[] = {
        "Speaker Playback Volume",
        "Mono Playback Volume",
        "Line-Out Playback Volume",
+       "PCM Playback Volume",
        NULL,
 };
 
@@ -1638,6 +1994,9 @@ static const char *alc_slave_sws[] = {
 /*
  * build control elements
  */
+
+static void alc_free_kctls(struct hda_codec *codec);
+
 static int alc_build_controls(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -1649,7 +2008,11 @@ static int alc_build_controls(struct hda_codec *codec)
                if (err < 0)
                        return err;
        }
-
+       if (spec->cap_mixer) {
+               err = snd_hda_add_new_ctls(codec, spec->cap_mixer);
+               if (err < 0)
+                       return err;
+       }
        if (spec->multiout.dig_out_nid) {
                err = snd_hda_create_spdif_out_ctls(codec,
                                                    spec->multiout.dig_out_nid);
@@ -1684,6 +2047,7 @@ static int alc_build_controls(struct hda_codec *codec)
                        return err;
        }
 
+       alc_free_kctls(codec); /* no longer needed */
        return 0;
 }
 
@@ -2774,19 +3138,27 @@ static int alc_build_pcms(struct hda_codec *codec)
        return 0;
 }
 
+static void alc_free_kctls(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       if (spec->kctls.list) {
+               struct snd_kcontrol_new *kctl = spec->kctls.list;
+               int i;
+               for (i = 0; i < spec->kctls.used; i++)
+                       kfree(kctl[i].name);
+       }
+       snd_array_free(&spec->kctls);
+}
+
 static void alc_free(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int i;
 
        if (!spec)
                return;
 
-       if (spec->kctl_alloc) {
-               for (i = 0; i < spec->num_kctl_used; i++)
-                       kfree(spec->kctl_alloc[i].name);
-               kfree(spec->kctl_alloc);
-       }
+       alc_free_kctls(codec);
        kfree(spec);
        codec->spec = NULL; /* to be sure */
 }
@@ -3268,6 +3640,8 @@ static struct alc_config_preset alc880_presets[] = {
                                alc880_gpio2_init_verbs },
                .num_dacs = ARRAY_SIZE(alc880_dac_nids),
                .dac_nids = alc880_dac_nids,
+               .adc_nids = alc880_adc_nids_alt, /* FIXME: correct? */
+               .num_adc_nids = 1, /* single ADC */
                .hp_nid = 0x03,
                .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
                .channel_mode = alc880_2_jack_modes,
@@ -3532,9 +3906,6 @@ static struct alc_config_preset alc880_presets[] = {
  * Automatic parse of I/O pins from the BIOS configuration
  */
 
-#define NUM_CONTROL_ALLOC      32
-#define NUM_VERB_ALLOC         32
-
 enum {
        ALC_CTL_WIDGET_VOL,
        ALC_CTL_WIDGET_MUTE,
@@ -3552,29 +3923,15 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
 {
        struct snd_kcontrol_new *knew;
 
-       if (spec->num_kctl_used >= spec->num_kctl_alloc) {
-               int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
-               /* array + terminator */
-               knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL);
-               if (!knew)
-                       return -ENOMEM;
-               if (spec->kctl_alloc) {
-                       memcpy(knew, spec->kctl_alloc,
-                              sizeof(*knew) * spec->num_kctl_alloc);
-                       kfree(spec->kctl_alloc);
-               }
-               spec->kctl_alloc = knew;
-               spec->num_kctl_alloc = num;
-       }
-
-       knew = &spec->kctl_alloc[spec->num_kctl_used];
+       snd_array_init(&spec->kctls, sizeof(*knew), 32);
+       knew = snd_array_new(&spec->kctls);
+       if (!knew)
+               return -ENOMEM;
        *knew = alc880_control_templates[type];
        knew->name = kstrdup(name, GFP_KERNEL);
        if (!knew->name)
                return -ENOMEM;
        knew->private_value = val;
-       spec->num_kctl_used++;
        return 0;
 }
 
@@ -3898,10 +4255,10 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
        if (spec->autocfg.dig_in_pin)
                spec->dig_in_nid = ALC880_DIGIN_NID;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               add_mixer(spec, spec->kctls.list);
 
-       spec->init_verbs[spec->num_init_verbs++] = alc880_volume_init_verbs;
+       add_verb(spec, alc880_volume_init_verbs);
 
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux;
@@ -3925,6 +4282,17 @@ static void alc880_auto_init(struct hda_codec *codec)
  * OK, here we have finally the patch for ALC880
  */
 
+static void set_capture_mixer(struct alc_spec *spec)
+{
+       static struct snd_kcontrol_new *caps[3] = {
+               alc_capture_mixer1,
+               alc_capture_mixer2,
+               alc_capture_mixer3,
+       };
+       if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3)
+               spec->cap_mixer = caps[spec->num_adc_nids - 1];
+}
+
 static int patch_alc880(struct hda_codec *codec)
 {
        struct alc_spec *spec;
@@ -3980,16 +4348,12 @@ static int patch_alc880(struct hda_codec *codec)
                if (wcap != AC_WID_AUD_IN) {
                        spec->adc_nids = alc880_adc_nids_alt;
                        spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt);
-                       spec->mixers[spec->num_mixers] =
-                               alc880_capture_alt_mixer;
-                       spec->num_mixers++;
                } else {
                        spec->adc_nids = alc880_adc_nids;
                        spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
-                       spec->mixers[spec->num_mixers] = alc880_capture_mixer;
-                       spec->num_mixers++;
                }
        }
+       set_capture_mixer(spec);
 
        spec->vmaster_nid = 0x0c;
 
@@ -4000,6 +4364,7 @@ static int patch_alc880(struct hda_codec *codec)
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc880_loopbacks;
 #endif
+       codec->proc_widget_hook = print_realtek_coef;
 
        return 0;
 }
@@ -4024,11 +4389,6 @@ static hda_nid_t alc260_adc_nids_alt[1] = {
        0x05,
 };
 
-static hda_nid_t alc260_hp_adc_nids[2] = {
-       /* ADC1, 0 */
-       0x05, 0x04
-};
-
 /* NIDs used when simultaneous access to both ADCs makes sense.  Note that
  * alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC.
  */
@@ -4157,13 +4517,13 @@ static void alc260_hp_master_update(struct hda_codec *codec,
        struct alc_spec *spec = codec->spec;
        unsigned int val = spec->master_sw ? PIN_HP : 0;
        /* change HP and line-out pins */
-       snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+       snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
                            val);
-       snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+       snd_hda_codec_write(codec, line, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
                            val);
        /* mono (speaker) depending on the HP jack sense */
        val = (val && !spec->jack_present) ? PIN_OUT : 0;
-       snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+       snd_hda_codec_write(codec, mono, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
                            val);
 }
 
@@ -4242,7 +4602,7 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
                .info = snd_ctl_boolean_mono_info,
                .get = alc260_hp_master_sw_get,
                .put = alc260_hp_master_sw_put,
-               .private_value = (0x10 << 16) | (0x15 << 8) | 0x11
+               .private_value = (0x15 << 16) | (0x10 << 8) | 0x11
        },
        HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
@@ -4295,7 +4655,7 @@ static void alc260_hp_3013_automute(struct hda_codec *codec)
        present = snd_hda_codec_read(codec, 0x15, 0,
                                     AC_VERB_GET_PIN_SENSE, 0);
        spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
-       alc260_hp_master_update(codec, 0x10, 0x15, 0x11);
+       alc260_hp_master_update(codec, 0x15, 0x10, 0x11);
 }
 
 static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
@@ -4427,45 +4787,6 @@ static struct snd_kcontrol_new alc260_replacer_672v_mixer[] = {
        { } /* end */
 };
 
-/* capture mixer elements */
-static struct snd_kcontrol_new alc260_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x05, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x05, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = alc_mux_enum_info,
-               .get = alc_mux_enum_get,
-               .put = alc_mux_enum_put,
-       },
-       { } /* end */
-};
-
-static struct snd_kcontrol_new alc260_capture_alt_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x05, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x05, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = alc_mux_enum_info,
-               .get = alc_mux_enum_get,
-               .put = alc_mux_enum_put,
-       },
-       { } /* end */
-};
-
 /*
  * initialization verbs
  */
@@ -5282,7 +5603,6 @@ static struct hda_verb alc260_volume_init_verbs[] = {
 static int alc260_parse_auto_config(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int wcap;
        int err;
        static hda_nid_t alc260_ignore[] = { 0x17, 0 };
 
@@ -5293,7 +5613,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
        err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg);
        if (err < 0)
                return err;
-       if (!spec->kctl_alloc)
+       if (!spec->kctls.list)
                return 0; /* can't find valid BIOS pin config */
        err = alc260_auto_create_analog_input_ctls(spec, &spec->autocfg);
        if (err < 0)
@@ -5303,28 +5623,14 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
 
        if (spec->autocfg.dig_out_pin)
                spec->multiout.dig_out_nid = ALC260_DIGOUT_NID;
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               add_mixer(spec, spec->kctls.list);
 
-       spec->init_verbs[spec->num_init_verbs++] = alc260_volume_init_verbs;
+       add_verb(spec, alc260_volume_init_verbs);
 
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux;
 
-       /* check whether NID 0x04 is valid */
-       wcap = get_wcaps(codec, 0x04);
-       wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
-       if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
-               spec->adc_nids = alc260_adc_nids_alt;
-               spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt);
-               spec->mixers[spec->num_mixers] = alc260_capture_alt_mixer;
-       } else {
-               spec->adc_nids = alc260_adc_nids;
-               spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
-               spec->mixers[spec->num_mixers] = alc260_capture_mixer;
-       }
-       spec->num_mixers++;
-
        store_pin_configs(codec);
        return 1;
 }
@@ -5394,12 +5700,11 @@ static struct alc_config_preset alc260_presets[] = {
        [ALC260_BASIC] = {
                .mixers = { alc260_base_output_mixer,
                            alc260_input_mixer,
-                           alc260_pc_beep_mixer,
-                           alc260_capture_mixer },
+                           alc260_pc_beep_mixer },
                .init_verbs = { alc260_init_verbs },
                .num_dacs = ARRAY_SIZE(alc260_dac_nids),
                .dac_nids = alc260_dac_nids,
-               .num_adc_nids = ARRAY_SIZE(alc260_adc_nids),
+               .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
                .adc_nids = alc260_adc_nids,
                .num_channel_mode = ARRAY_SIZE(alc260_modes),
                .channel_mode = alc260_modes,
@@ -5407,14 +5712,13 @@ static struct alc_config_preset alc260_presets[] = {
        },
        [ALC260_HP] = {
                .mixers = { alc260_hp_output_mixer,
-                           alc260_input_mixer,
-                           alc260_capture_alt_mixer },
+                           alc260_input_mixer },
                .init_verbs = { alc260_init_verbs,
                                alc260_hp_unsol_verbs },
                .num_dacs = ARRAY_SIZE(alc260_dac_nids),
                .dac_nids = alc260_dac_nids,
-               .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
-               .adc_nids = alc260_hp_adc_nids,
+               .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
+               .adc_nids = alc260_adc_nids_alt,
                .num_channel_mode = ARRAY_SIZE(alc260_modes),
                .channel_mode = alc260_modes,
                .input_mux = &alc260_capture_source,
@@ -5423,14 +5727,13 @@ static struct alc_config_preset alc260_presets[] = {
        },
        [ALC260_HP_DC7600] = {
                .mixers = { alc260_hp_dc7600_mixer,
-                           alc260_input_mixer,
-                           alc260_capture_alt_mixer },
+                           alc260_input_mixer },
                .init_verbs = { alc260_init_verbs,
                                alc260_hp_dc7600_verbs },
                .num_dacs = ARRAY_SIZE(alc260_dac_nids),
                .dac_nids = alc260_dac_nids,
-               .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
-               .adc_nids = alc260_hp_adc_nids,
+               .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
+               .adc_nids = alc260_adc_nids_alt,
                .num_channel_mode = ARRAY_SIZE(alc260_modes),
                .channel_mode = alc260_modes,
                .input_mux = &alc260_capture_source,
@@ -5439,14 +5742,13 @@ static struct alc_config_preset alc260_presets[] = {
        },
        [ALC260_HP_3013] = {
                .mixers = { alc260_hp_3013_mixer,
-                           alc260_input_mixer,
-                           alc260_capture_alt_mixer },
+                           alc260_input_mixer },
                .init_verbs = { alc260_hp_3013_init_verbs,
                                alc260_hp_3013_unsol_verbs },
                .num_dacs = ARRAY_SIZE(alc260_dac_nids),
                .dac_nids = alc260_dac_nids,
-               .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
-               .adc_nids = alc260_hp_adc_nids,
+               .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
+               .adc_nids = alc260_adc_nids_alt,
                .num_channel_mode = ARRAY_SIZE(alc260_modes),
                .channel_mode = alc260_modes,
                .input_mux = &alc260_capture_source,
@@ -5454,8 +5756,7 @@ static struct alc_config_preset alc260_presets[] = {
                .init_hook = alc260_hp_3013_automute,
        },
        [ALC260_FUJITSU_S702X] = {
-               .mixers = { alc260_fujitsu_mixer,
-                           alc260_capture_mixer },
+               .mixers = { alc260_fujitsu_mixer },
                .init_verbs = { alc260_fujitsu_init_verbs },
                .num_dacs = ARRAY_SIZE(alc260_dac_nids),
                .dac_nids = alc260_dac_nids,
@@ -5467,8 +5768,7 @@ static struct alc_config_preset alc260_presets[] = {
                .input_mux = alc260_fujitsu_capture_sources,
        },
        [ALC260_ACER] = {
-               .mixers = { alc260_acer_mixer,
-                           alc260_capture_mixer },
+               .mixers = { alc260_acer_mixer },
                .init_verbs = { alc260_acer_init_verbs },
                .num_dacs = ARRAY_SIZE(alc260_dac_nids),
                .dac_nids = alc260_dac_nids,
@@ -5480,8 +5780,7 @@ static struct alc_config_preset alc260_presets[] = {
                .input_mux = alc260_acer_capture_sources,
        },
        [ALC260_WILL] = {
-               .mixers = { alc260_will_mixer,
-                           alc260_capture_mixer },
+               .mixers = { alc260_will_mixer },
                .init_verbs = { alc260_init_verbs, alc260_will_verbs },
                .num_dacs = ARRAY_SIZE(alc260_dac_nids),
                .dac_nids = alc260_dac_nids,
@@ -5493,8 +5792,7 @@ static struct alc_config_preset alc260_presets[] = {
                .input_mux = &alc260_capture_source,
        },
        [ALC260_REPLACER_672V] = {
-               .mixers = { alc260_replacer_672v_mixer,
-                           alc260_capture_mixer },
+               .mixers = { alc260_replacer_672v_mixer },
                .init_verbs = { alc260_init_verbs, alc260_replacer_672v_verbs },
                .num_dacs = ARRAY_SIZE(alc260_dac_nids),
                .dac_nids = alc260_dac_nids,
@@ -5509,8 +5807,7 @@ static struct alc_config_preset alc260_presets[] = {
        },
 #ifdef CONFIG_SND_DEBUG
        [ALC260_TEST] = {
-               .mixers = { alc260_test_mixer,
-                           alc260_capture_mixer },
+               .mixers = { alc260_test_mixer },
                .init_verbs = { alc260_test_init_verbs },
                .num_dacs = ARRAY_SIZE(alc260_test_dac_nids),
                .dac_nids = alc260_test_dac_nids,
@@ -5569,6 +5866,21 @@ static int patch_alc260(struct hda_codec *codec)
        spec->stream_digital_playback = &alc260_pcm_digital_playback;
        spec->stream_digital_capture = &alc260_pcm_digital_capture;
 
+       if (!spec->adc_nids && spec->input_mux) {
+               /* check whether NID 0x04 is valid */
+               unsigned int wcap = get_wcaps(codec, 0x04);
+               wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+               /* get type */
+               if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
+                       spec->adc_nids = alc260_adc_nids_alt;
+                       spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt);
+               } else {
+                       spec->adc_nids = alc260_adc_nids;
+                       spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
+               }
+       }
+       set_capture_mixer(spec);
+
        spec->vmaster_nid = 0x08;
 
        codec->patch_ops = alc_patch_ops;
@@ -5578,6 +5890,7 @@ static int patch_alc260(struct hda_codec *codec)
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc260_loopbacks;
 #endif
+       codec->proc_widget_hook = print_realtek_coef;
 
        return 0;
 }
@@ -5616,45 +5929,15 @@ static hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 };
 /* input MUX */
 /* FIXME: should be a matrix-type input source selection */
 
-static struct hda_input_mux alc882_capture_source = {
-       .num_items = 4,
-       .items = {
-               { "Mic", 0x0 },
-               { "Front Mic", 0x1 },
-               { "Line", 0x2 },
-               { "CD", 0x4 },
-       },
-};
-#define alc882_mux_enum_info alc_mux_enum_info
-#define alc882_mux_enum_get alc_mux_enum_get
-
-static int alc882_mux_enum_put(struct snd_kcontrol *kcontrol,
-                              struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct alc_spec *spec = codec->spec;
-       const struct hda_input_mux *imux = spec->input_mux;
-       unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-       hda_nid_t nid = spec->capsrc_nids ?
-               spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx];
-       unsigned int *cur_val = &spec->cur_mux[adc_idx];
-       unsigned int i, idx;
-
-       idx = ucontrol->value.enumerated.item[0];
-       if (idx >= imux->num_items)
-               idx = imux->num_items - 1;
-       if (*cur_val == idx)
-               return 0;
-       for (i = 0; i < imux->num_items; i++) {
-               unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
-               snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
-                                        imux->items[i].index,
-                                        HDA_AMP_MUTE, v);
-       }
-       *cur_val = idx;
-       return 1;
-}
-
+static struct hda_input_mux alc882_capture_source = {
+       .num_items = 4,
+       .items = {
+               { "Mic", 0x0 },
+               { "Front Mic", 0x1 },
+               { "Line", 0x2 },
+               { "CD", 0x4 },
+       },
+};
 /*
  * 2ch mode
  */
@@ -6337,49 +6620,6 @@ static struct hda_verb alc882_auto_init_verbs[] = {
        { }
 };
 
-/* capture mixer elements */
-static struct snd_kcontrol_new alc882_capture_alt_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = alc882_mux_enum_info,
-               .get = alc882_mux_enum_get,
-               .put = alc882_mux_enum_put,
-       },
-       { } /* end */
-};
-
-static struct snd_kcontrol_new alc882_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 3,
-               .info = alc882_mux_enum_info,
-               .get = alc882_mux_enum_get,
-               .put = alc882_mux_enum_put,
-       },
-       { } /* end */
-};
-
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #define alc882_loopbacks       alc880_loopbacks
 #endif
@@ -6508,8 +6748,7 @@ static struct alc_config_preset alc882_presets[] = {
                .init_hook = alc885_imac24_init_hook,
        },
        [ALC882_TARGA] = {
-               .mixers = { alc882_targa_mixer, alc882_chmode_mixer,
-                           alc882_capture_mixer },
+               .mixers = { alc882_targa_mixer, alc882_chmode_mixer },
                .init_verbs = { alc882_init_verbs, alc882_targa_verbs},
                .num_dacs = ARRAY_SIZE(alc882_dac_nids),
                .dac_nids = alc882_dac_nids,
@@ -6525,8 +6764,7 @@ static struct alc_config_preset alc882_presets[] = {
                .init_hook = alc882_targa_automute,
        },
        [ALC882_ASUS_A7J] = {
-               .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer,
-                           alc882_capture_mixer },
+               .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer },
                .init_verbs = { alc882_init_verbs, alc882_asus_a7j_verbs},
                .num_dacs = ARRAY_SIZE(alc882_dac_nids),
                .dac_nids = alc882_dac_nids,
@@ -6831,6 +7069,7 @@ static int patch_alc882(struct hda_codec *codec)
        spec->stream_digital_playback = &alc882_pcm_digital_playback;
        spec->stream_digital_capture = &alc882_pcm_digital_capture;
 
+       spec->is_mix_capture = 1; /* matrix-style capture */
        if (!spec->adc_nids && spec->input_mux) {
                /* check whether NID 0x07 is valid */
                unsigned int wcap = get_wcaps(codec, 0x07);
@@ -6840,17 +7079,13 @@ static int patch_alc882(struct hda_codec *codec)
                        spec->adc_nids = alc882_adc_nids_alt;
                        spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt);
                        spec->capsrc_nids = alc882_capsrc_nids_alt;
-                       spec->mixers[spec->num_mixers] =
-                               alc882_capture_alt_mixer;
-                       spec->num_mixers++;
                } else {
                        spec->adc_nids = alc882_adc_nids;
                        spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids);
                        spec->capsrc_nids = alc882_capsrc_nids;
-                       spec->mixers[spec->num_mixers] = alc882_capture_mixer;
-                       spec->num_mixers++;
                }
        }
+       set_capture_mixer(spec);
 
        spec->vmaster_nid = 0x0c;
 
@@ -6861,6 +7096,7 @@ static int patch_alc882(struct hda_codec *codec)
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc882_loopbacks;
 #endif
+       codec->proc_widget_hook = print_realtek_coef;
 
        return 0;
 }
@@ -6879,6 +7115,8 @@ static int patch_alc882(struct hda_codec *codec)
 #define ALC883_DIGOUT_NID      0x06
 #define ALC883_DIGIN_NID       0x0a
 
+#define ALC1200_DIGOUT_NID     0x10
+
 static hda_nid_t alc883_dac_nids[4] = {
        /* front, rear, clfe, rear_surr */
        0x02, 0x03, 0x04, 0x05
@@ -6889,8 +7127,20 @@ static hda_nid_t alc883_adc_nids[2] = {
        0x08, 0x09,
 };
 
+static hda_nid_t alc883_adc_nids_alt[1] = {
+       /* ADC1 */
+       0x08,
+};
+
+static hda_nid_t alc883_adc_nids_rev[2] = {
+       /* ADC2-1 */
+       0x09, 0x08
+};
+
 static hda_nid_t alc883_capsrc_nids[2] = { 0x23, 0x22 };
 
+static hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 };
+
 /* input MUX */
 /* FIXME: should be a matrix-type input source selection */
 
@@ -6957,11 +7207,6 @@ static struct hda_input_mux alc883_asus_eee1601_capture_source = {
        },
 };
 
-#define alc883_mux_enum_info alc_mux_enum_info
-#define alc883_mux_enum_get alc_mux_enum_get
-/* ALC883 has the ALC882-type input selection */
-#define alc883_mux_enum_put alc882_mux_enum_put
-
 /*
  * 2ch mode
  */
@@ -7115,19 +7360,6 @@ static struct snd_kcontrol_new alc883_base_mixer[] = {
        HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
        HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
        HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = alc883_mux_enum_info,
-               .get = alc883_mux_enum_get,
-               .put = alc883_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -7145,19 +7377,6 @@ static struct snd_kcontrol_new alc883_mitac_mixer[] = {
        HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
        HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
        HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = alc883_mux_enum_info,
-               .get = alc883_mux_enum_get,
-               .put = alc883_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -7172,19 +7391,6 @@ static struct snd_kcontrol_new alc883_clevo_m720_mixer[] = {
        HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
        HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
        HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = alc883_mux_enum_info,
-               .get = alc883_mux_enum_get,
-               .put = alc883_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -7199,19 +7405,6 @@ static struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = {
        HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
        HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
        HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = alc883_mux_enum_info,
-               .get = alc883_mux_enum_get,
-               .put = alc883_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -7231,19 +7424,6 @@ static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = {
        HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
        HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
        HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = alc883_mux_enum_info,
-               .get = alc883_mux_enum_get,
-               .put = alc883_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -7269,17 +7449,6 @@ static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = {
        HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
        HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
        HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = alc883_mux_enum_info,
-               .get = alc883_mux_enum_get,
-               .put = alc883_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -7306,19 +7475,6 @@ static struct snd_kcontrol_new alc883_3ST_6ch_intel_mixer[] = {
        HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
        HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = alc883_mux_enum_info,
-               .get = alc883_mux_enum_get,
-               .put = alc883_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -7344,18 +7500,6 @@ static struct snd_kcontrol_new alc883_fivestack_mixer[] = {
        HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
        HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
        HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = alc883_mux_enum_info,
-               .get = alc883_mux_enum_get,
-               .put = alc883_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -7376,19 +7520,6 @@ static struct snd_kcontrol_new alc883_tagra_mixer[] = {
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
        HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = alc883_mux_enum_info,
-               .get = alc883_mux_enum_get,
-               .put = alc883_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -7404,19 +7535,6 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = {
        HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
        HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
        HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = alc883_mux_enum_info,
-               .get = alc883_mux_enum_get,
-               .put = alc883_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -7429,17 +7547,6 @@ static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = {
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
        HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = alc883_mux_enum_info,
-               .get = alc883_mux_enum_get,
-               .put = alc883_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -7453,19 +7560,6 @@ static struct snd_kcontrol_new alc883_lenovo_nb0763_mixer[] = {
        HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("iMic Playback Volume", 0x0b, 0x1, HDA_INPUT),
        HDA_CODEC_MUTE("iMic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = alc883_mux_enum_info,
-               .get = alc883_mux_enum_get,
-               .put = alc883_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -7479,19 +7573,6 @@ static struct snd_kcontrol_new alc883_medion_md2_mixer[] = {
        HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
        HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = alc883_mux_enum_info,
-               .get = alc883_mux_enum_get,
-               .put = alc883_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -7504,19 +7585,6 @@ static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
        HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = alc883_mux_enum_info,
-               .get = alc883_mux_enum_get,
-               .put = alc883_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -7544,19 +7612,6 @@ static struct snd_kcontrol_new alc888_lenovo_sky_mixer[] = {
        HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
        HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
        HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = alc883_mux_enum_info,
-               .get = alc883_mux_enum_get,
-               .put = alc883_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -7587,6 +7642,10 @@ static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = {
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
        HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       { } /* end */
+};
+
+static struct snd_kcontrol_new alc883_asus_eee1601_cap_mixer[] = {
        HDA_BIND_VOL("Capture Volume", &alc883_bind_cap_vol),
        HDA_BIND_SW("Capture Switch", &alc883_bind_cap_switch),
        {
@@ -7594,9 +7653,9 @@ static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = {
                /* .name = "Capture Source", */
                .name = "Input Source",
                .count = 1,
-               .info = alc883_mux_enum_info,
-               .get = alc883_mux_enum_get,
-               .put = alc883_mux_enum_put,
+               .info = alc_mux_enum_info,
+               .get = alc_mux_enum_get,
+               .put = alc_mux_enum_put,
        },
        { } /* end */
 };
@@ -8251,27 +8310,6 @@ static struct hda_verb alc883_auto_init_verbs[] = {
        { }
 };
 
-/* capture mixer elements */
-static struct snd_kcontrol_new alc883_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = alc882_mux_enum_info,
-               .get = alc882_mux_enum_get,
-               .put = alc882_mux_enum_put,
-       },
-       { } /* end */
-};
-
 static struct hda_verb alc888_asus_m90v_verbs[] = {
        {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
        {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
@@ -8394,6 +8432,7 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
        [ALC883_TARGA_2ch_DIG]  = "targa-2ch-dig",
        [ALC883_ACER]           = "acer",
        [ALC883_ACER_ASPIRE]    = "acer-aspire",
+       [ALC888_ACER_ASPIRE_4930G]      = "acer-aspire-4930g",
        [ALC883_MEDION]         = "medion",
        [ALC883_MEDION_MD2]     = "medion-md2",
        [ALC883_LAPTOP_EAPD]    = "laptop-eapd",
@@ -8407,7 +8446,9 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
        [ALC883_MITAC]          = "mitac",
        [ALC883_CLEVO_M720]     = "clevo-m720",
        [ALC883_FUJITSU_PI2515] = "fujitsu-pi2515",
+       [ALC888_FUJITSU_XA3530] = "fujitsu-xa3530",
        [ALC883_3ST_6ch_INTEL]  = "3stack-6ch-intel",
+       [ALC1200_ASUS_P5Q]      = "asus-p5q",
        [ALC883_AUTO]           = "auto",
 };
 
@@ -8418,6 +8459,8 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
        SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
        SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE),
+       SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G",
+               ALC888_ACER_ASPIRE_4930G),
        SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default Acer */
        SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),
        SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
@@ -8426,6 +8469,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG),
        SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V),
        SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
+       SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q),
        SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601),
        SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG),
        SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
@@ -8452,6 +8496,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
        SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
        SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
+       SND_PCI_QUIRK(0x1462, 0x7260, "MSI 7260", ALC883_TARGA_DIG),
        SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG),
        SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
        SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
@@ -8463,6 +8508,8 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
        SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch),
        SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
        SND_PCI_QUIRK(0x1734, 0x1108, "Fujitsu AMILO Pi2515", ALC883_FUJITSU_PI2515),
+       SND_PCI_QUIRK(0x1734, 0x113d, "Fujitsu AMILO Xa3530",
+               ALC888_FUJITSU_XA3530),
        SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch),
        SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
        SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
@@ -8553,6 +8600,8 @@ static struct alc_config_preset alc883_presets[] = {
                .init_verbs = { alc883_init_verbs, alc883_tagra_verbs},
                .num_dacs = ARRAY_SIZE(alc883_dac_nids),
                .dac_nids = alc883_dac_nids,
+               .adc_nids = alc883_adc_nids_alt,
+               .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
                .dig_out_nid = ALC883_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
                .channel_mode = alc883_3ST_2ch_modes,
@@ -8586,6 +8635,26 @@ static struct alc_config_preset alc883_presets[] = {
                .unsol_event = alc883_acer_aspire_unsol_event,
                .init_hook = alc883_acer_aspire_automute,
        },
+       [ALC888_ACER_ASPIRE_4930G] = {
+               .mixers = { alc888_base_mixer,
+                               alc883_chmode_mixer },
+               .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
+                               alc888_acer_aspire_4930g_verbs },
+               .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+               .dac_nids = alc883_dac_nids,
+               .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
+               .adc_nids = alc883_adc_nids_rev,
+               .capsrc_nids = alc883_capsrc_nids_rev,
+               .dig_out_nid = ALC883_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
+               .channel_mode = alc883_3ST_6ch_modes,
+               .need_dac_fix = 1,
+               .num_mux_defs =
+                       ARRAY_SIZE(alc888_2_capture_sources),
+               .input_mux = alc888_2_capture_sources,
+               .unsol_event = alc888_acer_aspire_4930g_unsol_event,
+               .init_hook = alc888_acer_aspire_4930g_automute,
+       },
        [ALC883_MEDION] = {
                .mixers = { alc883_fivestack_mixer,
                            alc883_chmode_mixer },
@@ -8593,6 +8662,8 @@ static struct alc_config_preset alc883_presets[] = {
                                alc883_medion_eapd_verbs },
                .num_dacs = ARRAY_SIZE(alc883_dac_nids),
                .dac_nids = alc883_dac_nids,
+               .adc_nids = alc883_adc_nids_alt,
+               .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
                .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
                .channel_mode = alc883_sixstack_modes,
                .input_mux = &alc883_capture_source,
@@ -8635,6 +8706,8 @@ static struct alc_config_preset alc883_presets[] = {
                .init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs},
                .num_dacs = ARRAY_SIZE(alc883_dac_nids),
                .dac_nids = alc883_dac_nids,
+               .adc_nids = alc883_adc_nids_alt,
+               .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
                .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_lenovo_101e_capture_source,
@@ -8725,14 +8798,30 @@ static struct alc_config_preset alc883_presets[] = {
                .unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event,
                .init_hook = alc883_2ch_fujitsu_pi2515_automute,
        },
+       [ALC888_FUJITSU_XA3530] = {
+               .mixers = { alc888_base_mixer, alc883_chmode_mixer },
+               .init_verbs = { alc883_init_verbs,
+                       alc888_fujitsu_xa3530_verbs },
+               .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+               .dac_nids = alc883_dac_nids,
+               .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
+               .adc_nids = alc883_adc_nids_rev,
+               .capsrc_nids = alc883_capsrc_nids_rev,
+               .dig_out_nid = ALC883_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc888_4ST_8ch_intel_modes),
+               .channel_mode = alc888_4ST_8ch_intel_modes,
+               .num_mux_defs =
+                       ARRAY_SIZE(alc888_2_capture_sources),
+               .input_mux = alc888_2_capture_sources,
+               .unsol_event = alc888_fujitsu_xa3530_unsol_event,
+               .init_hook = alc888_fujitsu_xa3530_automute,
+       },
        [ALC888_LENOVO_SKY] = {
                .mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer },
                .init_verbs = { alc883_init_verbs, alc888_lenovo_sky_verbs},
                .num_dacs = ARRAY_SIZE(alc883_dac_nids),
                .dac_nids = alc883_dac_nids,
                .dig_out_nid = ALC883_DIGOUT_NID,
-               .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
-               .adc_nids = alc883_adc_nids,
                .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
                .channel_mode = alc883_sixstack_modes,
                .need_dac_fix = 1,
@@ -8756,6 +8845,7 @@ static struct alc_config_preset alc883_presets[] = {
        },
        [ALC888_ASUS_EEE1601] = {
                .mixers = { alc883_asus_eee1601_mixer },
+               .cap_mixer = alc883_asus_eee1601_cap_mixer,
                .init_verbs = { alc883_init_verbs, alc888_asus_eee1601_verbs },
                .num_dacs = ARRAY_SIZE(alc883_dac_nids),
                .dac_nids = alc883_dac_nids,
@@ -8768,6 +8858,17 @@ static struct alc_config_preset alc883_presets[] = {
                .unsol_event = alc883_eee1601_unsol_event,
                .init_hook = alc883_eee1601_inithook,
        },
+       [ALC1200_ASUS_P5Q] = {
+               .mixers = { alc883_base_mixer, alc883_chmode_mixer },
+               .init_verbs = { alc883_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+               .dac_nids = alc883_dac_nids,
+               .dig_out_nid = ALC1200_DIGOUT_NID,
+               .dig_in_nid = ALC883_DIGIN_NID,
+               .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+               .channel_mode = alc883_sixstack_modes,
+               .input_mux = &alc883_capture_source,
+       },
 };
 
 
@@ -8862,8 +8963,6 @@ static int alc883_parse_auto_config(struct hda_codec *codec)
 
        /* hack - override the init verbs */
        spec->init_verbs[0] = alc883_auto_init_verbs;
-       spec->mixers[spec->num_mixers] = alc883_capture_mixer;
-       spec->num_mixers++;
 
        return 1; /* config found */
 }
@@ -8946,9 +9045,15 @@ static int patch_alc883(struct hda_codec *codec)
        spec->stream_digital_playback = &alc883_pcm_digital_playback;
        spec->stream_digital_capture = &alc883_pcm_digital_capture;
 
-       spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
-       spec->adc_nids = alc883_adc_nids;
-       spec->capsrc_nids = alc883_capsrc_nids;
+       if (!spec->num_adc_nids) {
+               spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
+               spec->adc_nids = alc883_adc_nids;
+       }
+       if (!spec->capsrc_nids)
+               spec->capsrc_nids = alc883_capsrc_nids;
+       spec->is_mix_capture = 1; /* matrix-style capture */
+       if (!spec->cap_mixer)
+               set_capture_mixer(spec);
 
        spec->vmaster_nid = 0x0c;
 
@@ -8960,6 +9065,7 @@ static int patch_alc883(struct hda_codec *codec)
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc883_loopbacks;
 #endif
+       codec->proc_widget_hook = print_realtek_coef;
 
        return 0;
 }
@@ -9439,20 +9545,6 @@ static struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = {
        HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = alc_mux_enum_info,
-               .get = alc_mux_enum_get,
-               .put = alc_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -9969,7 +10061,7 @@ static int alc262_ultra_mux_enum_put(struct snd_kcontrol *kcontrol,
        struct alc_spec *spec = codec->spec;
        int ret;
 
-       ret = alc882_mux_enum_put(kcontrol, ucontrol);
+       ret = alc_mux_enum_put(kcontrol, ucontrol);
        if (!ret)
                return 0;
        /* reprogram the HP pin as mic or HP according to the input source */
@@ -9986,8 +10078,8 @@ static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Capture Source",
-               .info = alc882_mux_enum_info,
-               .get = alc882_mux_enum_get,
+               .info = alc_mux_enum_info,
+               .get = alc_mux_enum_get,
                .put = alc262_ultra_mux_enum_put,
        },
        { } /* end */
@@ -10380,10 +10472,10 @@ static int alc262_parse_auto_config(struct hda_codec *codec)
        if (spec->autocfg.dig_in_pin)
                spec->dig_in_nid = ALC262_DIGIN_NID;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               add_mixer(spec, spec->kctls.list);
 
-       spec->init_verbs[spec->num_init_verbs++] = alc262_volume_init_verbs;
+       add_verb(spec, alc262_volume_init_verbs);
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux;
 
@@ -10466,6 +10558,8 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = {
        SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
        SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
        SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
+       SND_PCI_QUIRK(0x104d, 0x9033, "Sony VAIO VGN-SR19XN",
+                     ALC262_SONY_ASSAMD),
        SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
                      ALC262_TOSHIBA_RX1),
        SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06),
@@ -10624,7 +10718,8 @@ static struct alc_config_preset alc262_presets[] = {
                .init_hook = alc262_hippo_automute,
        },
        [ALC262_ULTRA] = {
-               .mixers = { alc262_ultra_mixer, alc262_ultra_capture_mixer },
+               .mixers = { alc262_ultra_mixer },
+               .cap_mixer = alc262_ultra_capture_mixer,
                .init_verbs = { alc262_ultra_verbs },
                .num_dacs = ARRAY_SIZE(alc262_dac_nids),
                .dac_nids = alc262_dac_nids,
@@ -10750,6 +10845,7 @@ static int patch_alc262(struct hda_codec *codec)
        spec->stream_digital_playback = &alc262_pcm_digital_playback;
        spec->stream_digital_capture = &alc262_pcm_digital_capture;
 
+       spec->is_mix_capture = 1;
        if (!spec->adc_nids && spec->input_mux) {
                /* check whether NID 0x07 is valid */
                unsigned int wcap = get_wcaps(codec, 0x07);
@@ -10760,17 +10856,14 @@ static int patch_alc262(struct hda_codec *codec)
                        spec->adc_nids = alc262_adc_nids_alt;
                        spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt);
                        spec->capsrc_nids = alc262_capsrc_nids_alt;
-                       spec->mixers[spec->num_mixers] =
-                               alc262_capture_alt_mixer;
-                       spec->num_mixers++;
                } else {
                        spec->adc_nids = alc262_adc_nids;
                        spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids);
                        spec->capsrc_nids = alc262_capsrc_nids;
-                       spec->mixers[spec->num_mixers] = alc262_capture_mixer;
-                       spec->num_mixers++;
                }
        }
+       if (!spec->cap_mixer)
+               set_capture_mixer(spec);
 
        spec->vmaster_nid = 0x0c;
 
@@ -10781,6 +10874,7 @@ static int patch_alc262(struct hda_codec *codec)
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc262_loopbacks;
 #endif
+       codec->proc_widget_hook = print_realtek_coef;
 
        return 0;
 }
@@ -10942,6 +11036,22 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = {
        { }
 };
 
+static struct snd_kcontrol_new alc268_acer_dmic_mixer[] = {
+       /* output mixer control */
+       HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Switch",
+               .info = snd_hda_mixer_amp_switch_info,
+               .get = snd_hda_mixer_amp_switch_get,
+               .put = alc268_acer_master_sw_put,
+               .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+       },
+       HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT),
+       { }
+};
+
 static struct hda_verb alc268_acer_aspire_one_verbs[] = {
        {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
        {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
@@ -11218,10 +11328,6 @@ static struct hda_verb alc268_volume_init_verbs[] = {
        { }
 };
 
-#define alc268_mux_enum_info alc_mux_enum_info
-#define alc268_mux_enum_get alc_mux_enum_get
-#define alc268_mux_enum_put alc_mux_enum_put
-
 static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
        HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
@@ -11233,9 +11339,9 @@ static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
                /* .name = "Capture Source", */
                .name = "Input Source",
                .count = 1,
-               .info = alc268_mux_enum_info,
-               .get = alc268_mux_enum_get,
-               .put = alc268_mux_enum_put,
+               .info = alc_mux_enum_info,
+               .get = alc_mux_enum_get,
+               .put = alc_mux_enum_put,
        },
        { } /* end */
 };
@@ -11253,9 +11359,9 @@ static struct snd_kcontrol_new alc268_capture_mixer[] = {
                /* .name = "Capture Source", */
                .name = "Input Source",
                .count = 2,
-               .info = alc268_mux_enum_info,
-               .get = alc268_mux_enum_get,
-               .put = alc268_mux_enum_put,
+               .info = alc_mux_enum_info,
+               .get = alc_mux_enum_get,
+               .put = alc_mux_enum_put,
        },
        { } /* end */
 };
@@ -11271,6 +11377,15 @@ static struct hda_input_mux alc268_capture_source = {
 };
 
 static struct hda_input_mux alc268_acer_capture_source = {
+       .num_items = 3,
+       .items = {
+               { "Mic", 0x0 },
+               { "Internal Mic", 0x1 },
+               { "Line", 0x2 },
+       },
+};
+
+static struct hda_input_mux alc268_acer_dmic_capture_source = {
        .num_items = 3,
        .items = {
                { "Mic", 0x0 },
@@ -11512,13 +11627,13 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
        if (spec->autocfg.dig_out_pin)
                spec->multiout.dig_out_nid = ALC268_DIGOUT_NID;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               add_mixer(spec, spec->kctls.list);
 
        if (spec->autocfg.speaker_pins[0] != 0x1d)
-               spec->mixers[spec->num_mixers++] = alc268_beep_mixer;
+               add_mixer(spec, alc268_beep_mixer);
 
-       spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs;
+       add_verb(spec, alc268_volume_init_verbs);
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux;
 
@@ -11554,6 +11669,7 @@ static const char *alc268_models[ALC268_MODEL_LAST] = {
        [ALC268_3ST]            = "3stack",
        [ALC268_TOSHIBA]        = "toshiba",
        [ALC268_ACER]           = "acer",
+       [ALC268_ACER_DMIC]      = "acer-dmic",
        [ALC268_ACER_ASPIRE_ONE]        = "acer-aspire",
        [ALC268_DELL]           = "dell",
        [ALC268_ZEPTO]          = "zepto",
@@ -11649,6 +11765,23 @@ static struct alc_config_preset alc268_presets[] = {
                .unsol_event = alc268_acer_unsol_event,
                .init_hook = alc268_acer_init_hook,
        },
+       [ALC268_ACER_DMIC] = {
+               .mixers = { alc268_acer_dmic_mixer, alc268_capture_alt_mixer,
+                           alc268_beep_mixer },
+               .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+                               alc268_acer_verbs },
+               .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+               .dac_nids = alc268_dac_nids,
+               .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+               .adc_nids = alc268_adc_nids_alt,
+               .capsrc_nids = alc268_capsrc_nids,
+               .hp_nid = 0x02,
+               .num_channel_mode = ARRAY_SIZE(alc268_modes),
+               .channel_mode = alc268_modes,
+               .input_mux = &alc268_acer_dmic_capture_source,
+               .unsol_event = alc268_acer_unsol_event,
+               .init_hook = alc268_acer_init_hook,
+       },
        [ALC268_ACER_ASPIRE_ONE] = {
                .mixers = { alc268_acer_aspire_one_mixer,
                                alc268_capture_alt_mixer },
@@ -11787,15 +11920,11 @@ static int patch_alc268(struct hda_codec *codec)
                if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
                        spec->adc_nids = alc268_adc_nids_alt;
                        spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt);
-                       spec->mixers[spec->num_mixers] =
-                                       alc268_capture_alt_mixer;
-                       spec->num_mixers++;
+                       add_mixer(spec, alc268_capture_alt_mixer);
                } else {
                        spec->adc_nids = alc268_adc_nids;
                        spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids);
-                       spec->mixers[spec->num_mixers] =
-                               alc268_capture_mixer;
-                       spec->num_mixers++;
+                       add_mixer(spec, alc268_capture_mixer);
                }
                spec->capsrc_nids = alc268_capsrc_nids;
                /* set default input source */
@@ -11811,6 +11940,8 @@ static int patch_alc268(struct hda_codec *codec)
        if (board_config == ALC268_AUTO)
                spec->init_hook = alc268_auto_init;
 
+       codec->proc_widget_hook = print_realtek_coef;
+
        return 0;
 }
 
@@ -11893,6 +12024,31 @@ static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = {
        { }
 };
 
+static struct snd_kcontrol_new alc269_lifebook_mixer[] = {
+       /* output mixer control */
+       HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Switch",
+               .info = snd_hda_mixer_amp_switch_info,
+               .get = snd_hda_mixer_amp_switch_get,
+               .put = alc268_acer_master_sw_put,
+               .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+       },
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x0b, 0x03, HDA_INPUT),
+       HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x0b, 0x03, HDA_INPUT),
+       HDA_CODEC_VOLUME("Dock Mic Boost", 0x1b, 0, HDA_INPUT),
+       HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x04, HDA_INPUT),
+       { }
+};
+
 /* bind volumes of both NID 0x0c and 0x0d */
 static struct hda_bind_ctls alc269_epc_bind_vol = {
        .ops = &snd_hda_bind_vol,
@@ -11911,28 +12067,18 @@ static struct snd_kcontrol_new alc269_eeepc_mixer[] = {
 };
 
 /* capture mixer elements */
-static struct snd_kcontrol_new alc269_capture_mixer[] = {
+static struct snd_kcontrol_new alc269_epc_capture_mixer[] = {
        HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = alc_mux_enum_info,
-               .get = alc_mux_enum_get,
-               .put = alc_mux_enum_put,
-       },
+       HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
        { } /* end */
 };
 
-/* capture mixer elements */
-static struct snd_kcontrol_new alc269_epc_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+/* FSC amilo */
+static struct snd_kcontrol_new alc269_fujitsu_mixer[] = {
+       HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+       HDA_BIND_VOL("PCM Playback Volume", &alc269_epc_bind_vol),
        { } /* end */
 };
 
@@ -11953,6 +12099,20 @@ static struct hda_verb alc269_quanta_fl1_verbs[] = {
        { }
 };
 
+static struct hda_verb alc269_lifebook_verbs[] = {
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+       {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
+       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+       {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       { }
+};
+
 /* toggle speaker-output according to the hp-jack state */
 static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
 {
@@ -11978,6 +12138,37 @@ static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
                        AC_VERB_SET_PROC_COEF, 0x480);
 }
 
+/* toggle speaker-output according to the hp-jacks state */
+static void alc269_lifebook_speaker_automute(struct hda_codec *codec)
+{
+       unsigned int present;
+       unsigned char bits;
+
+       /* Check laptop headphone socket */
+       present = snd_hda_codec_read(codec, 0x15, 0,
+                       AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+       /* Check port replicator headphone socket */
+       present |= snd_hda_codec_read(codec, 0x1a, 0,
+                       AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+       bits = present ? AMP_IN_MUTE(0) : 0;
+       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+                       AMP_IN_MUTE(0), bits);
+       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+                       AMP_IN_MUTE(0), bits);
+
+       snd_hda_codec_write(codec, 0x20, 0,
+                       AC_VERB_SET_COEF_INDEX, 0x0c);
+       snd_hda_codec_write(codec, 0x20, 0,
+                       AC_VERB_SET_PROC_COEF, 0x680);
+
+       snd_hda_codec_write(codec, 0x20, 0,
+                       AC_VERB_SET_COEF_INDEX, 0x0c);
+       snd_hda_codec_write(codec, 0x20, 0,
+                       AC_VERB_SET_PROC_COEF, 0x480);
+}
+
 static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec)
 {
        unsigned int present;
@@ -11988,6 +12179,29 @@ static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec)
                            AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x1);
 }
 
+static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
+{
+       unsigned int present_laptop;
+       unsigned int present_dock;
+
+       present_laptop = snd_hda_codec_read(codec, 0x18, 0,
+                               AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+       present_dock = snd_hda_codec_read(codec, 0x1b, 0,
+                               AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+       /* Laptop mic port overrides dock mic port, design decision */
+       if (present_dock)
+               snd_hda_codec_write(codec, 0x23, 0,
+                               AC_VERB_SET_CONNECT_SEL, 0x3);
+       if (present_laptop)
+               snd_hda_codec_write(codec, 0x23, 0,
+                               AC_VERB_SET_CONNECT_SEL, 0x0);
+       if (!present_dock && !present_laptop)
+               snd_hda_codec_write(codec, 0x23, 0,
+                               AC_VERB_SET_CONNECT_SEL, 0x1);
+}
+
 static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec,
                                    unsigned int res)
 {
@@ -11997,12 +12211,27 @@ static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec,
                alc269_quanta_fl1_mic_automute(codec);
 }
 
+static void alc269_lifebook_unsol_event(struct hda_codec *codec,
+                                       unsigned int res)
+{
+       if ((res >> 26) == ALC880_HP_EVENT)
+               alc269_lifebook_speaker_automute(codec);
+       if ((res >> 26) == ALC880_MIC_EVENT)
+               alc269_lifebook_mic_autoswitch(codec);
+}
+
 static void alc269_quanta_fl1_init_hook(struct hda_codec *codec)
 {
        alc269_quanta_fl1_speaker_automute(codec);
        alc269_quanta_fl1_mic_automute(codec);
 }
 
+static void alc269_lifebook_init_hook(struct hda_codec *codec)
+{
+       alc269_lifebook_speaker_automute(codec);
+       alc269_lifebook_mic_autoswitch(codec);
+}
+
 static struct hda_verb alc269_eeepc_dmic_init_verbs[] = {
        {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
        {0x23, AC_VERB_SET_CONNECT_SEL, 0x05},
@@ -12303,17 +12532,17 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
        if (spec->autocfg.dig_out_pin)
                spec->multiout.dig_out_nid = ALC269_DIGOUT_NID;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               add_mixer(spec, spec->kctls.list);
 
        /* create a beep mixer control if the pin 0x1d isn't assigned */
        for (i = 0; i < ARRAY_SIZE(spec->autocfg.input_pins); i++)
                if (spec->autocfg.input_pins[i] == 0x1d)
                        break;
        if (i >= ARRAY_SIZE(spec->autocfg.input_pins))
-               spec->mixers[spec->num_mixers++] = alc269_beep_mixer;
+               add_mixer(spec, alc269_beep_mixer);
 
-       spec->init_verbs[spec->num_init_verbs++] = alc269_init_verbs;
+       add_verb(spec, alc269_init_verbs);
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux;
        /* set default input source */
@@ -12325,8 +12554,8 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
        if (err < 0)
                return err;
 
-       spec->mixers[spec->num_mixers] = alc269_capture_mixer;
-       spec->num_mixers++;
+       if (!spec->cap_mixer)
+               set_capture_mixer(spec);
 
        store_pin_configs(codec);
        return 1;
@@ -12355,7 +12584,9 @@ static const char *alc269_models[ALC269_MODEL_LAST] = {
        [ALC269_BASIC]                  = "basic",
        [ALC269_QUANTA_FL1]             = "quanta",
        [ALC269_ASUS_EEEPC_P703]        = "eeepc-p703",
-       [ALC269_ASUS_EEEPC_P901]        = "eeepc-p901"
+       [ALC269_ASUS_EEEPC_P901]        = "eeepc-p901",
+       [ALC269_FUJITSU]                = "fujitsu",
+       [ALC269_LIFEBOOK]               = "lifebook"
 };
 
 static struct snd_pci_quirk alc269_cfg_tbl[] = {
@@ -12366,12 +12597,14 @@ static struct snd_pci_quirk alc269_cfg_tbl[] = {
                      ALC269_ASUS_EEEPC_P901),
        SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101",
                      ALC269_ASUS_EEEPC_P901),
+       SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU),
+       SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK),
        {}
 };
 
 static struct alc_config_preset alc269_presets[] = {
        [ALC269_BASIC] = {
-               .mixers = { alc269_base_mixer, alc269_capture_mixer },
+               .mixers = { alc269_base_mixer },
                .init_verbs = { alc269_init_verbs },
                .num_dacs = ARRAY_SIZE(alc269_dac_nids),
                .dac_nids = alc269_dac_nids,
@@ -12393,7 +12626,8 @@ static struct alc_config_preset alc269_presets[] = {
                .init_hook = alc269_quanta_fl1_init_hook,
        },
        [ALC269_ASUS_EEEPC_P703] = {
-               .mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer },
+               .mixers = { alc269_eeepc_mixer },
+               .cap_mixer = alc269_epc_capture_mixer,
                .init_verbs = { alc269_init_verbs,
                                alc269_eeepc_amic_init_verbs },
                .num_dacs = ARRAY_SIZE(alc269_dac_nids),
@@ -12406,7 +12640,22 @@ static struct alc_config_preset alc269_presets[] = {
                .init_hook = alc269_eeepc_amic_inithook,
        },
        [ALC269_ASUS_EEEPC_P901] = {
-               .mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer},
+               .mixers = { alc269_eeepc_mixer },
+               .cap_mixer = alc269_epc_capture_mixer,
+               .init_verbs = { alc269_init_verbs,
+                               alc269_eeepc_dmic_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc269_dac_nids),
+               .dac_nids = alc269_dac_nids,
+               .hp_nid = 0x03,
+               .num_channel_mode = ARRAY_SIZE(alc269_modes),
+               .channel_mode = alc269_modes,
+               .input_mux = &alc269_eeepc_dmic_capture_source,
+               .unsol_event = alc269_eeepc_dmic_unsol_event,
+               .init_hook = alc269_eeepc_dmic_inithook,
+       },
+       [ALC269_FUJITSU] = {
+               .mixers = { alc269_fujitsu_mixer, alc269_beep_mixer },
+               .cap_mixer = alc269_epc_capture_mixer,
                .init_verbs = { alc269_init_verbs,
                                alc269_eeepc_dmic_init_verbs },
                .num_dacs = ARRAY_SIZE(alc269_dac_nids),
@@ -12418,6 +12667,18 @@ static struct alc_config_preset alc269_presets[] = {
                .unsol_event = alc269_eeepc_dmic_unsol_event,
                .init_hook = alc269_eeepc_dmic_inithook,
        },
+       [ALC269_LIFEBOOK] = {
+               .mixers = { alc269_lifebook_mixer },
+               .init_verbs = { alc269_init_verbs, alc269_lifebook_verbs },
+               .num_dacs = ARRAY_SIZE(alc269_dac_nids),
+               .dac_nids = alc269_dac_nids,
+               .hp_nid = 0x03,
+               .num_channel_mode = ARRAY_SIZE(alc269_modes),
+               .channel_mode = alc269_modes,
+               .input_mux = &alc269_capture_source,
+               .unsol_event = alc269_lifebook_unsol_event,
+               .init_hook = alc269_lifebook_init_hook,
+       },
 };
 
 static int patch_alc269(struct hda_codec *codec)
@@ -12472,6 +12733,8 @@ static int patch_alc269(struct hda_codec *codec)
        spec->adc_nids = alc269_adc_nids;
        spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
        spec->capsrc_nids = alc269_capsrc_nids;
+       if (!spec->cap_mixer)
+               set_capture_mixer(spec);
 
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC269_AUTO)
@@ -12480,6 +12743,7 @@ static int patch_alc269(struct hda_codec *codec)
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc269_loopbacks;
 #endif
+       codec->proc_widget_hook = print_realtek_coef;
 
        return 0;
 }
@@ -12612,17 +12876,6 @@ static struct snd_kcontrol_new alc861_base_mixer[] = {
        HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
        HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
 
-        /* Capture mixer control */
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .count = 1,
-               .info = alc_mux_enum_info,
-               .get = alc_mux_enum_get,
-               .put = alc_mux_enum_put,
-       },
        { } /* end */
 };
 
@@ -12646,17 +12899,6 @@ static struct snd_kcontrol_new alc861_3ST_mixer[] = {
        HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
        HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
 
-       /* Capture mixer control */
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .count = 1,
-               .info = alc_mux_enum_info,
-               .get = alc_mux_enum_get,
-               .put = alc_mux_enum_put,
-       },
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Channel Mode",
@@ -12674,18 +12916,6 @@ static struct snd_kcontrol_new alc861_toshiba_mixer[] = {
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
        HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
 
-        /*Capture mixer control */
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .count = 1,
-               .info = alc_mux_enum_info,
-               .get = alc_mux_enum_get,
-               .put = alc_mux_enum_put,
-       },
-
        { } /* end */
 };
 
@@ -12709,17 +12939,6 @@ static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = {
        HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
        HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
 
-       /* Capture mixer control */
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .count = 1,
-               .info = alc_mux_enum_info,
-               .get = alc_mux_enum_get,
-               .put = alc_mux_enum_put,
-       },
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Channel Mode",
@@ -12751,17 +12970,6 @@ static struct snd_kcontrol_new alc861_asus_mixer[] = {
        HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
        HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_OUTPUT),
 
-       /* Capture mixer control */
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .count = 1,
-               .info = alc_mux_enum_info,
-               .get = alc_mux_enum_get,
-               .put = alc_mux_enum_put,
-       },
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Channel Mode",
@@ -13293,25 +13501,6 @@ static int alc861_auto_create_analog_input_ctls(struct alc_spec *spec,
        return 0;
 }
 
-static struct snd_kcontrol_new alc861_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = alc_mux_enum_info,
-               .get = alc_mux_enum_get,
-               .put = alc_mux_enum_put,
-       },
-       { } /* end */
-};
-
 static void alc861_auto_set_output_and_unmute(struct hda_codec *codec,
                                              hda_nid_t nid,
                                              int pin_type, int dac_idx)
@@ -13402,18 +13591,17 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
        if (spec->autocfg.dig_out_pin)
                spec->multiout.dig_out_nid = ALC861_DIGOUT_NID;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               add_mixer(spec, spec->kctls.list);
 
-       spec->init_verbs[spec->num_init_verbs++] = alc861_auto_init_verbs;
+       add_verb(spec, alc861_auto_init_verbs);
 
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux;
 
        spec->adc_nids = alc861_adc_nids;
        spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
-       spec->mixers[spec->num_mixers] = alc861_capture_mixer;
-       spec->num_mixers++;
+       set_capture_mixer(spec);
 
        store_pin_configs(codec);
        return 1;
@@ -13644,6 +13832,7 @@ static int patch_alc861(struct hda_codec *codec)
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc861_loopbacks;
 #endif
+       codec->proc_widget_hook = print_realtek_coef;
 
        return 0;
 }
@@ -13709,11 +13898,6 @@ static struct hda_input_mux alc861vd_hp_capture_source = {
        },
 };
 
-#define alc861vd_mux_enum_info alc_mux_enum_info
-#define alc861vd_mux_enum_get alc_mux_enum_get
-/* ALC861VD has the ALC882-type input selection (but has only one ADC) */
-#define alc861vd_mux_enum_put alc882_mux_enum_put
-
 /*
  * 2ch mode
  */
@@ -13759,25 +13943,6 @@ static struct snd_kcontrol_new alc861vd_chmode_mixer[] = {
        { } /* end */
 };
 
-static struct snd_kcontrol_new alc861vd_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
-
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = alc861vd_mux_enum_info,
-               .get = alc861vd_mux_enum_get,
-               .put = alc861vd_mux_enum_put,
-       },
-       { } /* end */
-};
-
 /* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
  *                 Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
  */
@@ -14169,6 +14334,7 @@ static void alc861vd_dallas_unsol_event(struct hda_codec *codec, unsigned int re
 static const char *alc861vd_models[ALC861VD_MODEL_LAST] = {
        [ALC660VD_3ST]          = "3stack-660",
        [ALC660VD_3ST_DIG]      = "3stack-660-digout",
+       [ALC660VD_ASUS_V1S]     = "asus-v1s",
        [ALC861VD_3ST]          = "3stack",
        [ALC861VD_3ST_DIG]      = "3stack-digout",
        [ALC861VD_6ST_DIG]      = "6stack-digout",
@@ -14183,7 +14349,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
        SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST),
        SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),
-       SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC861VD_LENOVO),
+       SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC660VD_ASUS_V1S),
        SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG),
        SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
        SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
@@ -14290,6 +14456,21 @@ static struct alc_config_preset alc861vd_presets[] = {
                .unsol_event = alc861vd_dallas_unsol_event,
                .init_hook = alc861vd_dallas_automute,
        },
+       [ALC660VD_ASUS_V1S] = {
+               .mixers = { alc861vd_lenovo_mixer },
+               .init_verbs = { alc861vd_volume_init_verbs,
+                               alc861vd_3stack_init_verbs,
+                               alc861vd_eapd_verbs,
+                               alc861vd_lenovo_unsol_verbs },
+               .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
+               .dac_nids = alc660vd_dac_nids,
+               .dig_out_nid = ALC861VD_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
+               .channel_mode = alc861vd_3stack_2ch_modes,
+               .input_mux = &alc861vd_capture_source,
+               .unsol_event = alc861vd_lenovo_unsol_event,
+               .init_hook = alc861vd_lenovo_automute,
+       },
 };
 
 /*
@@ -14514,11 +14695,10 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec)
        if (spec->autocfg.dig_out_pin)
                spec->multiout.dig_out_nid = ALC861VD_DIGOUT_NID;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               add_mixer(spec, spec->kctls.list);
 
-       spec->init_verbs[spec->num_init_verbs++]
-               = alc861vd_volume_init_verbs;
+       add_verb(spec, alc861vd_volume_init_verbs);
 
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux;
@@ -14585,7 +14765,7 @@ static int patch_alc861vd(struct hda_codec *codec)
                spec->stream_name_analog = "ALC660-VD Analog";
                spec->stream_name_digital = "ALC660-VD Digital";
                /* always turn on EAPD */
-               spec->init_verbs[spec->num_init_verbs++] = alc660vd_eapd_verbs;
+               add_verb(spec, alc660vd_eapd_verbs);
        } else {
                spec->stream_name_analog = "ALC861VD Analog";
                spec->stream_name_digital = "ALC861VD Digital";
@@ -14600,9 +14780,9 @@ static int patch_alc861vd(struct hda_codec *codec)
        spec->adc_nids = alc861vd_adc_nids;
        spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids);
        spec->capsrc_nids = alc861vd_capsrc_nids;
+       spec->is_mix_capture = 1;
 
-       spec->mixers[spec->num_mixers] = alc861vd_capture_mixer;
-       spec->num_mixers++;
+       set_capture_mixer(spec);
 
        spec->vmaster_nid = 0x02;
 
@@ -14614,6 +14794,7 @@ static int patch_alc861vd(struct hda_codec *codec)
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc861vd_loopbacks;
 #endif
+       codec->proc_widget_hook = print_realtek_coef;
 
        return 0;
 }
@@ -14689,10 +14870,6 @@ static struct hda_input_mux alc663_m51va_capture_source = {
        },
 };
 
-#define alc662_mux_enum_info alc_mux_enum_info
-#define alc662_mux_enum_get alc_mux_enum_get
-#define alc662_mux_enum_put alc882_mux_enum_put
-
 /*
  * 2ch mode
  */
@@ -15278,25 +15455,6 @@ static struct hda_verb alc662_ecs_init_verbs[] = {
        {}
 };
 
-/* capture mixer elements */
-static struct snd_kcontrol_new alc662_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = alc662_mux_enum_info,
-               .get = alc662_mux_enum_get,
-               .put = alc662_mux_enum_put,
-       },
-       { } /* end */
-};
-
 static struct snd_kcontrol_new alc662_auto_capture_mixer[] = {
        HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
@@ -15868,7 +16026,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = {
 
 static struct alc_config_preset alc662_presets[] = {
        [ALC662_3ST_2ch_DIG] = {
-               .mixers = { alc662_3ST_2ch_mixer, alc662_capture_mixer },
+               .mixers = { alc662_3ST_2ch_mixer },
                .init_verbs = { alc662_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
@@ -15879,8 +16037,7 @@ static struct alc_config_preset alc662_presets[] = {
                .input_mux = &alc662_capture_source,
        },
        [ALC662_3ST_6ch_DIG] = {
-               .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer,
-                           alc662_capture_mixer },
+               .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer },
                .init_verbs = { alc662_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
@@ -15892,8 +16049,7 @@ static struct alc_config_preset alc662_presets[] = {
                .input_mux = &alc662_capture_source,
        },
        [ALC662_3ST_6ch] = {
-               .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer,
-                           alc662_capture_mixer },
+               .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer },
                .init_verbs = { alc662_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
@@ -15903,8 +16059,7 @@ static struct alc_config_preset alc662_presets[] = {
                .input_mux = &alc662_capture_source,
        },
        [ALC662_5ST_DIG] = {
-               .mixers = { alc662_base_mixer, alc662_chmode_mixer,
-                           alc662_capture_mixer },
+               .mixers = { alc662_base_mixer, alc662_chmode_mixer },
                .init_verbs = { alc662_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
@@ -15915,7 +16070,7 @@ static struct alc_config_preset alc662_presets[] = {
                .input_mux = &alc662_capture_source,
        },
        [ALC662_LENOVO_101E] = {
-               .mixers = { alc662_lenovo_101e_mixer, alc662_capture_mixer },
+               .mixers = { alc662_lenovo_101e_mixer },
                .init_verbs = { alc662_init_verbs, alc662_sue_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
@@ -15926,7 +16081,7 @@ static struct alc_config_preset alc662_presets[] = {
                .init_hook = alc662_lenovo_101e_all_automute,
        },
        [ALC662_ASUS_EEEPC_P701] = {
-               .mixers = { alc662_eeepc_p701_mixer, alc662_capture_mixer },
+               .mixers = { alc662_eeepc_p701_mixer },
                .init_verbs = { alc662_init_verbs,
                                alc662_eeepc_sue_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -15938,7 +16093,7 @@ static struct alc_config_preset alc662_presets[] = {
                .init_hook = alc662_eeepc_inithook,
        },
        [ALC662_ASUS_EEEPC_EP20] = {
-               .mixers = { alc662_eeepc_ep20_mixer, alc662_capture_mixer,
+               .mixers = { alc662_eeepc_ep20_mixer,
                            alc662_chmode_mixer },
                .init_verbs = { alc662_init_verbs,
                                alc662_eeepc_ep20_sue_init_verbs },
@@ -15951,7 +16106,7 @@ static struct alc_config_preset alc662_presets[] = {
                .init_hook = alc662_eeepc_ep20_inithook,
        },
        [ALC662_ECS] = {
-               .mixers = { alc662_ecs_mixer, alc662_capture_mixer },
+               .mixers = { alc662_ecs_mixer },
                .init_verbs = { alc662_init_verbs,
                                alc662_ecs_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -15963,7 +16118,7 @@ static struct alc_config_preset alc662_presets[] = {
                .init_hook = alc662_eeepc_inithook,
        },
        [ALC663_ASUS_M51VA] = {
-               .mixers = { alc663_m51va_mixer, alc662_capture_mixer},
+               .mixers = { alc663_m51va_mixer },
                .init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
@@ -15975,7 +16130,7 @@ static struct alc_config_preset alc662_presets[] = {
                .init_hook = alc663_m51va_inithook,
        },
        [ALC663_ASUS_G71V] = {
-               .mixers = { alc663_g71v_mixer, alc662_capture_mixer},
+               .mixers = { alc663_g71v_mixer },
                .init_verbs = { alc662_init_verbs, alc663_g71v_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
@@ -15987,7 +16142,7 @@ static struct alc_config_preset alc662_presets[] = {
                .init_hook = alc663_g71v_inithook,
        },
        [ALC663_ASUS_H13] = {
-               .mixers = { alc663_m51va_mixer, alc662_capture_mixer},
+               .mixers = { alc663_m51va_mixer },
                .init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
@@ -15998,7 +16153,7 @@ static struct alc_config_preset alc662_presets[] = {
                .init_hook = alc663_m51va_inithook,
        },
        [ALC663_ASUS_G50V] = {
-               .mixers = { alc663_g50v_mixer, alc662_capture_mixer},
+               .mixers = { alc663_g50v_mixer },
                .init_verbs = { alc662_init_verbs, alc663_g50v_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
@@ -16010,7 +16165,8 @@ static struct alc_config_preset alc662_presets[] = {
                .init_hook = alc663_g50v_inithook,
        },
        [ALC663_ASUS_MODE1] = {
-               .mixers = { alc663_m51va_mixer, alc662_auto_capture_mixer },
+               .mixers = { alc663_m51va_mixer },
+               .cap_mixer = alc662_auto_capture_mixer,
                .init_verbs = { alc662_init_verbs,
                                alc663_21jd_amic_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16024,7 +16180,8 @@ static struct alc_config_preset alc662_presets[] = {
                .init_hook = alc663_mode1_inithook,
        },
        [ALC662_ASUS_MODE2] = {
-               .mixers = { alc662_1bjd_mixer, alc662_auto_capture_mixer },
+               .mixers = { alc662_1bjd_mixer },
+               .cap_mixer = alc662_auto_capture_mixer,
                .init_verbs = { alc662_init_verbs,
                                alc662_1bjd_amic_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16037,7 +16194,8 @@ static struct alc_config_preset alc662_presets[] = {
                .init_hook = alc662_mode2_inithook,
        },
        [ALC663_ASUS_MODE3] = {
-               .mixers = { alc663_two_hp_m1_mixer, alc662_auto_capture_mixer },
+               .mixers = { alc663_two_hp_m1_mixer },
+               .cap_mixer = alc662_auto_capture_mixer,
                .init_verbs = { alc662_init_verbs,
                                alc663_two_hp_amic_m1_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16051,8 +16209,8 @@ static struct alc_config_preset alc662_presets[] = {
                .init_hook = alc663_mode3_inithook,
        },
        [ALC663_ASUS_MODE4] = {
-               .mixers = { alc663_asus_21jd_clfe_mixer,
-                               alc662_auto_capture_mixer},
+               .mixers = { alc663_asus_21jd_clfe_mixer },
+               .cap_mixer = alc662_auto_capture_mixer,
                .init_verbs = { alc662_init_verbs,
                                alc663_21jd_amic_init_verbs},
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16066,8 +16224,8 @@ static struct alc_config_preset alc662_presets[] = {
                .init_hook = alc663_mode4_inithook,
        },
        [ALC663_ASUS_MODE5] = {
-               .mixers = { alc663_asus_15jd_clfe_mixer,
-                               alc662_auto_capture_mixer },
+               .mixers = { alc663_asus_15jd_clfe_mixer },
+               .cap_mixer = alc662_auto_capture_mixer,
                .init_verbs = { alc662_init_verbs,
                                alc663_15jd_amic_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16081,7 +16239,8 @@ static struct alc_config_preset alc662_presets[] = {
                .init_hook = alc663_mode5_inithook,
        },
        [ALC663_ASUS_MODE6] = {
-               .mixers = { alc663_two_hp_m2_mixer, alc662_auto_capture_mixer },
+               .mixers = { alc663_two_hp_m2_mixer },
+               .cap_mixer = alc662_auto_capture_mixer,
                .init_verbs = { alc662_init_verbs,
                                alc663_two_hp_amic_m2_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16342,24 +16501,20 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
        if (spec->autocfg.dig_out_pin)
                spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               add_mixer(spec, spec->kctls.list);
 
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux;
 
-       spec->init_verbs[spec->num_init_verbs++] = alc662_auto_init_verbs;
+       add_verb(spec, alc662_auto_init_verbs);
        if (codec->vendor_id == 0x10ec0663)
-               spec->init_verbs[spec->num_init_verbs++] =
-                       alc663_auto_init_verbs;
+               add_verb(spec, alc663_auto_init_verbs);
 
        err = alc_auto_add_mic_boost(codec);
        if (err < 0)
                return err;
 
-       spec->mixers[spec->num_mixers] = alc662_capture_mixer;
-       spec->num_mixers++;
-
        store_pin_configs(codec);
        return 1;
 }
@@ -16435,6 +16590,10 @@ static int patch_alc662(struct hda_codec *codec)
        spec->adc_nids = alc662_adc_nids;
        spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
        spec->capsrc_nids = alc662_capsrc_nids;
+       spec->is_mix_capture = 1;
+
+       if (!spec->cap_mixer)
+               set_capture_mixer(spec);
 
        spec->vmaster_nid = 0x02;
 
@@ -16445,6 +16604,7 @@ static int patch_alc662(struct hda_codec *codec)
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc662_loopbacks;
 #endif
+       codec->proc_widget_hook = print_realtek_coef;
 
        return 0;
 }
@@ -16452,7 +16612,7 @@ static int patch_alc662(struct hda_codec *codec)
 /*
  * patch entries
  */
-struct hda_codec_preset snd_hda_preset_realtek[] = {
+static struct hda_codec_preset snd_hda_preset_realtek[] = {
        { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
        { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
        { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
@@ -16484,3 +16644,26 @@ struct hda_codec_preset snd_hda_preset_realtek[] = {
        { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 },
        {} /* terminator */
 };
+
+MODULE_ALIAS("snd-hda-codec-id:10ec*");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek HD-audio codec");
+
+static struct hda_codec_preset_list realtek_list = {
+       .preset = snd_hda_preset_realtek,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_realtek_init(void)
+{
+       return snd_hda_add_codec_preset(&realtek_list);
+}
+
+static void __exit patch_realtek_exit(void)
+{
+       snd_hda_delete_codec_preset(&realtek_list);
+}
+
+module_init(patch_realtek_init)
+module_exit(patch_realtek_exit)
index 9332b63..43b436c 100644 (file)
@@ -28,7 +28,6 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 
 /* si3054 verbs */
 #define SI3054_VERB_READ_NODE  0x900
@@ -283,7 +282,7 @@ static int patch_si3054(struct hda_codec *codec)
 /*
  * patch entries
  */
-struct hda_codec_preset snd_hda_preset_si3054[] = {
+static struct hda_codec_preset snd_hda_preset_si3054[] = {
        { .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
        { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
        { .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
@@ -301,3 +300,35 @@ struct hda_codec_preset snd_hda_preset_si3054[] = {
        {}
 };
 
+MODULE_ALIAS("snd-hda-codec-id:163c3055");
+MODULE_ALIAS("snd-hda-codec-id:163c3155");
+MODULE_ALIAS("snd-hda-codec-id:11c13026");
+MODULE_ALIAS("snd-hda-codec-id:11c13055");
+MODULE_ALIAS("snd-hda-codec-id:11c13155");
+MODULE_ALIAS("snd-hda-codec-id:10573055");
+MODULE_ALIAS("snd-hda-codec-id:10573057");
+MODULE_ALIAS("snd-hda-codec-id:10573155");
+MODULE_ALIAS("snd-hda-codec-id:11063288");
+MODULE_ALIAS("snd-hda-codec-id:15433155");
+MODULE_ALIAS("snd-hda-codec-id:18540018");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Si3054 HD-audio modem codec");
+
+static struct hda_codec_preset_list si3054_list = {
+       .preset = snd_hda_preset_si3054,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_si3054_init(void)
+{
+       return snd_hda_add_codec_preset(&si3054_list);
+}
+
+static void __exit patch_si3054_exit(void)
+{
+       snd_hda_delete_codec_preset(&si3054_list);
+}
+
+module_init(patch_si3054_init)
+module_exit(patch_si3054_exit)
index b77f330..35b83dc 100644 (file)
 #include <linux/pci.h>
 #include <sound/core.h>
 #include <sound/asoundef.h>
+#include <sound/jack.h>
 #include "hda_codec.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 #include "hda_beep.h"
 
-#define NUM_CONTROL_ALLOC      32
-
-#define STAC_VREF_EVENT                0x00
-#define STAC_INSERT_EVENT      0x10
-#define STAC_PWR_EVENT         0x20
-#define STAC_HP_EVENT          0x30
+enum {
+       STAC_VREF_EVENT = 1,
+       STAC_INSERT_EVENT,
+       STAC_PWR_EVENT,
+       STAC_HP_EVENT,
+};
 
 enum {
        STAC_REF,
@@ -137,6 +137,19 @@ enum {
        STAC_927X_MODELS
 };
 
+struct sigmatel_event {
+       hda_nid_t nid;
+       unsigned char type;
+       unsigned char tag;
+       int data;
+};
+
+struct sigmatel_jack {
+       hda_nid_t nid;
+       int type;
+       struct snd_jack *jack;
+};
+
 struct sigmatel_spec {
        struct snd_kcontrol_new *mixers[4];
        unsigned int num_mixers;
@@ -144,8 +157,6 @@ struct sigmatel_spec {
        int board_config;
        unsigned int eapd_switch: 1;
        unsigned int surr_switch: 1;
-       unsigned int line_switch: 1;
-       unsigned int mic_switch: 1;
        unsigned int alt_switch: 1;
        unsigned int hp_detect: 1;
        unsigned int spdif_mute: 1;
@@ -170,12 +181,20 @@ struct sigmatel_spec {
        hda_nid_t *pwr_nids;
        hda_nid_t *dac_list;
 
+       /* jack detection */
+       struct snd_array jacks;
+
+       /* events */
+       struct snd_array events;
+
        /* playback */
        struct hda_input_mux *mono_mux;
        struct hda_input_mux *amp_mux;
        unsigned int cur_mmux;
        struct hda_multi_out multiout;
        hda_nid_t dac_nids[5];
+       hda_nid_t hp_dacs[5];
+       hda_nid_t speaker_dacs[5];
 
        /* capture */
        hda_nid_t *adc_nids;
@@ -199,7 +218,6 @@ struct sigmatel_spec {
        hda_nid_t *pin_nids;
        unsigned int num_pins;
        unsigned int *pin_configs;
-       unsigned int *bios_pin_configs;
 
        /* codec specific stuff */
        struct hda_verb *init;
@@ -220,15 +238,16 @@ struct sigmatel_spec {
        /* i/o switches */
        unsigned int io_switch[2];
        unsigned int clfe_swap;
-       unsigned int hp_switch; /* NID of HP as line-out */
+       hda_nid_t line_switch;  /* shared line-in for input and output */
+       hda_nid_t mic_switch;   /* shared mic-in for input and output */
+       hda_nid_t hp_switch; /* NID of HP as line-out */
        unsigned int aloopback;
 
        struct hda_pcm pcm_rec[2];      /* PCM information */
 
        /* dynamic controls and input_mux */
        struct auto_pin_cfg autocfg;
-       unsigned int num_kctl_alloc, num_kctl_used;
-       struct snd_kcontrol_new *kctl_alloc;
+       struct snd_array kctls;
        struct hda_input_mux private_dimux;
        struct hda_input_mux private_imux;
        struct hda_input_mux private_smux;
@@ -272,9 +291,6 @@ static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
 };
 
 #define STAC92HD73_DAC_COUNT 5
-static hda_nid_t stac92hd73xx_dac_nids[STAC92HD73_DAC_COUNT] = {
-       0x15, 0x16, 0x17, 0x18, 0x19,
-};
 
 static hda_nid_t stac92hd73xx_mux_nids[4] = {
        0x28, 0x29, 0x2a, 0x2b,
@@ -293,11 +309,7 @@ static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
        0x11, 0x12, 0
 };
 
-#define STAC92HD81_DAC_COUNT 2
 #define STAC92HD83_DAC_COUNT 3
-static hda_nid_t stac92hd83xxx_dac_nids[STAC92HD73_DAC_COUNT] = {
-       0x13, 0x14, 0x22,
-};
 
 static hda_nid_t stac92hd83xxx_dmux_nids[2] = {
        0x17, 0x18,
@@ -339,10 +351,6 @@ static hda_nid_t stac92hd71bxx_smux_nids[2] = {
        0x24, 0x25,
 };
 
-static hda_nid_t stac92hd71bxx_dac_nids[1] = {
-       0x10, /*0x11, */
-};
-
 #define STAC92HD71BXX_NUM_DMICS        2
 static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {
        0x18, 0x19, 0
@@ -574,12 +582,12 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol,
                else
                        nid = codec->slave_dig_outs[smux_idx - 1];
                if (spec->cur_smux[smux_idx] == smux->num_items - 1)
-                       val = AMP_OUT_MUTE;
+                       val = HDA_AMP_MUTE;
                else
-                       val = AMP_OUT_UNMUTE;
+                       val = 0;
                /* un/mute SPDIF out */
-               snd_hda_codec_write_cache(codec, nid, 0,
-                       AC_VERB_SET_AMP_GAIN_MUTE, val);
+               snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+                                        HDA_AMP_MUTE, val);
        }
        return 0;
 }
@@ -744,10 +752,6 @@ static struct hda_verb stac9200_eapd_init[] = {
 static struct hda_verb stac92hd73xx_6ch_core_init[] = {
        /* set master volume and direct control */
        { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-       /* setup audio connections */
-       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
-       { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
-       { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
        /* setup adcs to point to mixer */
        { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
        { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -766,10 +770,6 @@ static struct hda_verb dell_eq_core_init[] = {
        /* set master volume to max value without distortion
         * and direct control */
        { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
-       /* setup audio connections */
-       { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
-       { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x02},
-       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01},
        /* setup adcs to point to mixer */
        { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
        { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -783,10 +783,6 @@ static struct hda_verb dell_eq_core_init[] = {
 
 static struct hda_verb dell_m6_core_init[] = {
        { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-       /* setup audio connections */
-       { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
-       { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
-       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02},
        /* setup adcs to point to mixer */
        { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
        { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -801,13 +797,6 @@ static struct hda_verb dell_m6_core_init[] = {
 static struct hda_verb stac92hd73xx_8ch_core_init[] = {
        /* set master volume and direct control */
        { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-       /* setup audio connections */
-       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
-       { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
-       { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
-       /* connect hp ports to dac3 */
-       { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x03},
-       { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x03},
        /* setup adcs to point to mixer */
        { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
        { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -825,15 +814,8 @@ static struct hda_verb stac92hd73xx_8ch_core_init[] = {
 static struct hda_verb stac92hd73xx_10ch_core_init[] = {
        /* set master volume and direct control */
        { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-       /* setup audio connections */
-       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
-       { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01 },
-       { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02 },
        /* dac3 is connected to import3 mux */
        { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f},
-       /* connect hp ports to dac4 */
-       { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x04},
-       { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x04},
        /* setup adcs to point to mixer */
        { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
        { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -865,8 +847,6 @@ static struct hda_verb stac92hd83xxx_core_init[] = {
 static struct hda_verb stac92hd71bxx_core_init[] = {
        /* set master volume and direct control */
        { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-       /* connect headphone jack to dac1 */
-       { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
        /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
        { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -886,8 +866,6 @@ static struct hda_verb stac92hd71bxx_analog_core_init[] = {
 
        /* set master volume and direct control */
        { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-       /* connect headphone jack to dac1 */
-       { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
        /* unmute right and left channels for nodes 0x0a, 0xd */
        { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -1087,21 +1065,21 @@ static struct snd_kcontrol_new stac92hd83xxx_mixer[] = {
        HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_OUTPUT),
 
-       HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x1b, 0, HDA_INPUT),
-       HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0, HDA_INPUT),
+       HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x1b, 0x3, HDA_INPUT),
+       HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0x3, HDA_INPUT),
 
-       HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x1b, 0x1, HDA_INPUT),
-       HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x1, HDA_INPUT),
+       HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x1b, 0x4, HDA_INPUT),
+       HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x4, HDA_INPUT),
 
-       HDA_CODEC_VOLUME("Front Mic Capture Volume", 0x1b, 0x2, HDA_INPUT),
-       HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Capture Volume", 0x1b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x0, HDA_INPUT),
 
-       HDA_CODEC_VOLUME("Line In Capture Volume", 0x1b, 0x3, HDA_INPUT),
-       HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x3, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line In Capture Volume", 0x1b, 0x2, HDA_INPUT),
+       HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x2, HDA_INPUT),
 
        /*
-       HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x4, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x4, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x1, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x1, HDA_INPUT),
        */
        { } /* end */
 };
@@ -1240,9 +1218,14 @@ static const char *slave_sws[] = {
        NULL
 };
 
+static void stac92xx_free_kctls(struct hda_codec *codec);
+static int stac92xx_add_jack(struct hda_codec *codec, hda_nid_t nid, int type);
+
 static int stac92xx_build_controls(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       hda_nid_t nid;
        int err;
        int i;
 
@@ -1257,7 +1240,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
        }
        if (spec->num_dmuxes > 0) {
                stac_dmux_mixer.count = spec->num_dmuxes;
-               err = snd_ctl_add(codec->bus->card,
+               err = snd_hda_ctl_add(codec,
                                  snd_ctl_new1(&stac_dmux_mixer, codec));
                if (err < 0)
                        return err;
@@ -1273,7 +1256,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
                        spec->spdif_mute = 1;
                }
                stac_smux_mixer.count = spec->num_smuxes;
-               err = snd_ctl_add(codec->bus->card,
+               err = snd_hda_ctl_add(codec,
                                  snd_ctl_new1(&stac_smux_mixer, codec));
                if (err < 0)
                        return err;
@@ -1312,6 +1295,37 @@ static int stac92xx_build_controls(struct hda_codec *codec)
                        return err;
        }
 
+       stac92xx_free_kctls(codec); /* no longer needed */
+
+       /* create jack input elements */
+       if (spec->hp_detect) {
+               for (i = 0; i < cfg->hp_outs; i++) {
+                       int type = SND_JACK_HEADPHONE;
+                       nid = cfg->hp_pins[i];
+                       /* jack detection */
+                       if (cfg->hp_outs == i)
+                               type |= SND_JACK_LINEOUT;
+                       err = stac92xx_add_jack(codec, nid, type);
+                       if (err < 0)
+                               return err;
+               }
+       }
+       for (i = 0; i < cfg->line_outs; i++) {
+               err = stac92xx_add_jack(codec, cfg->line_out_pins[i],
+                                       SND_JACK_LINEOUT);
+               if (err < 0)
+                       return err;
+       }
+       for (i = 0; i < AUTO_PIN_LAST; i++) {
+               nid = cfg->input_pins[i];
+               if (nid) {
+                       err = stac92xx_add_jack(codec, nid,
+                                               SND_JACK_MICROPHONE);
+                       if (err < 0)
+                               return err;
+               }
+       }
+
        return 0;       
 }
 
@@ -1720,6 +1734,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
                      "HP dv5", STAC_HP_M4),
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f4,
                      "HP dv7", STAC_HP_M4),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fc,
+                     "HP dv7", STAC_HP_M4),
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a,
                                "unknown HP", STAC_HP_M4),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
@@ -2203,12 +2219,11 @@ static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
        int i;
        struct sigmatel_spec *spec = codec->spec;
        
-       if (! spec->bios_pin_configs) {
-               spec->bios_pin_configs = kcalloc(spec->num_pins,
-                                                sizeof(*spec->bios_pin_configs), GFP_KERNEL);
-               if (! spec->bios_pin_configs)
-                       return -ENOMEM;
-       }
+       kfree(spec->pin_configs);
+       spec->pin_configs = kcalloc(spec->num_pins, sizeof(*spec->pin_configs),
+                                   GFP_KERNEL);
+       if (!spec->pin_configs)
+               return -ENOMEM;
        
        for (i = 0; i < spec->num_pins; i++) {
                hda_nid_t nid = spec->pin_nids[i];
@@ -2218,7 +2233,7 @@ static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
                        AC_VERB_GET_CONFIG_DEFAULT, 0x00);      
                snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n",
                                        nid, pin_cfg);
-               spec->bios_pin_configs[i] = pin_cfg;
+               spec->pin_configs[i] = pin_cfg;
        }
        
        return 0;
@@ -2260,6 +2275,39 @@ static void stac92xx_set_config_regs(struct hda_codec *codec)
                                        spec->pin_configs[i]);
 }
 
+static int stac_save_pin_cfgs(struct hda_codec *codec, unsigned int *pins)
+{
+       struct sigmatel_spec *spec = codec->spec;
+
+       if (!pins)
+               return stac92xx_save_bios_config_regs(codec);
+
+       kfree(spec->pin_configs);
+       spec->pin_configs = kmemdup(pins,
+                                   spec->num_pins * sizeof(*pins),
+                                   GFP_KERNEL);
+       if (!spec->pin_configs)
+               return -ENOMEM;
+
+       stac92xx_set_config_regs(codec);
+       return 0;
+}
+
+static void stac_change_pin_config(struct hda_codec *codec, hda_nid_t nid,
+                                  unsigned int cfg)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; i < spec->num_pins; i++) {
+               if (spec->pin_nids[i] == nid) {
+                       spec->pin_configs[i] = cfg;
+                       stac92xx_set_config_reg(codec, nid, cfg);
+                       break;
+               }
+       }
+}
+
 /*
  * Analog playback callbacks
  */
@@ -2337,7 +2385,7 @@ static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
 
        if (spec->powerdown_adcs) {
                msleep(40);
-               snd_hda_codec_write_cache(codec, nid, 0,
+               snd_hda_codec_write(codec, nid, 0,
                        AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
        }
        snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
@@ -2353,7 +2401,7 @@ static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
 
        snd_hda_codec_cleanup_stream(codec, nid);
        if (spec->powerdown_adcs)
-               snd_hda_codec_write_cache(codec, nid, 0,
+               snd_hda_codec_write(codec, nid, 0,
                        AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
        return 0;
 }
@@ -2485,6 +2533,9 @@ static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
+static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid,
+                                  unsigned char type);
+
 static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
                        struct snd_ctl_elem_value *ucontrol)
 {
@@ -2497,7 +2548,7 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
        /* check to be sure that the ports are upto date with
         * switch changes
         */
-       codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+       stac_issue_unsol_event(codec, nid, STAC_HP_EVENT);
 
        return 1;
 }
@@ -2537,7 +2588,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
         * appropriately according to the pin direction
         */
        if (spec->hp_detect)
-               codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+               stac_issue_unsol_event(codec, nid, STAC_HP_EVENT);
 
         return 1;
 }
@@ -2632,28 +2683,16 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
 {
        struct snd_kcontrol_new *knew;
 
-       if (spec->num_kctl_used >= spec->num_kctl_alloc) {
-               int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
-               knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
-               if (! knew)
-                       return -ENOMEM;
-               if (spec->kctl_alloc) {
-                       memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
-                       kfree(spec->kctl_alloc);
-               }
-               spec->kctl_alloc = knew;
-               spec->num_kctl_alloc = num;
-       }
-
-       knew = &spec->kctl_alloc[spec->num_kctl_used];
+       snd_array_init(&spec->kctls, sizeof(*knew), 32);
+       knew = snd_array_new(&spec->kctls);
+       if (!knew)
+               return -ENOMEM;
        *knew = *ktemp;
        knew->index = idx;
        knew->name = kstrdup(name, GFP_KERNEL);
        if (!knew->name)
                return -ENOMEM;
        knew->private_value = val;
-       spec->num_kctl_used++;
        return 0;
 }
 
@@ -2674,70 +2713,53 @@ static inline int stac92xx_add_control(struct sigmatel_spec *spec, int type,
        return stac92xx_add_control_idx(spec, type, 0, name, val);
 }
 
-/* flag inputs as additional dynamic lineouts */
-static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg)
+/* check whether the line-input can be used as line-out */
+static hda_nid_t check_line_out_switch(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
-       unsigned int wcaps, wtype;
-       int i, num_dacs = 0;
-       
-       /* use the wcaps cache to count all DACs available for line-outs */
-       for (i = 0; i < codec->num_nodes; i++) {
-               wcaps = codec->wcaps[i];
-               wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       hda_nid_t nid;
+       unsigned int pincap;
 
-               if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL))
-                       num_dacs++;
-       }
+       if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
+               return 0;
+       nid = cfg->input_pins[AUTO_PIN_LINE];
+       pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+       if (pincap & AC_PINCAP_OUT)
+               return nid;
+       return 0;
+}
 
-       snd_printdd("%s: total dac count=%d\n", __func__, num_dacs);
-       
-       switch (cfg->line_outs) {
-       case 3:
-               /* add line-in as side */
-               if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 3) {
-                       cfg->line_out_pins[cfg->line_outs] =
-                               cfg->input_pins[AUTO_PIN_LINE];
-                       spec->line_switch = 1;
-                       cfg->line_outs++;
-               }
-               break;
-       case 2:
-               /* add line-in as clfe and mic as side */
-               if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 2) {
-                       cfg->line_out_pins[cfg->line_outs] =
-                               cfg->input_pins[AUTO_PIN_LINE];
-                       spec->line_switch = 1;
-                       cfg->line_outs++;
-               }
-               if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 3) {
-                       cfg->line_out_pins[cfg->line_outs] =
-                               cfg->input_pins[AUTO_PIN_MIC];
-                       spec->mic_switch = 1;
-                       cfg->line_outs++;
-               }
-               break;
-       case 1:
-               /* add line-in as surr and mic as clfe */
-               if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 1) {
-                       cfg->line_out_pins[cfg->line_outs] =
-                               cfg->input_pins[AUTO_PIN_LINE];
-                       spec->line_switch = 1;
-                       cfg->line_outs++;
-               }
-               if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 2) {
-                       cfg->line_out_pins[cfg->line_outs] =
-                               cfg->input_pins[AUTO_PIN_MIC];
-                       spec->mic_switch = 1;
-                       cfg->line_outs++;
+/* check whether the mic-input can be used as line-out */
+static hda_nid_t check_mic_out_switch(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       unsigned int def_conf, pincap;
+       unsigned int mic_pin;
+
+       if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
+               return 0;
+       mic_pin = AUTO_PIN_MIC;
+       for (;;) {
+               hda_nid_t nid = cfg->input_pins[mic_pin];
+               def_conf = snd_hda_codec_read(codec, nid, 0,
+                                             AC_VERB_GET_CONFIG_DEFAULT, 0);
+               /* some laptops have an internal analog microphone
+                * which can't be used as a output */
+               if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
+                       pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+                       if (pincap & AC_PINCAP_OUT)
+                               return nid;
                }
-               break;
+               if (mic_pin == AUTO_PIN_MIC)
+                       mic_pin = AUTO_PIN_FRONT_MIC;
+               else
+                       break;
        }
-
        return 0;
 }
 
-
 static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
 {
        int i;
@@ -2750,6 +2772,52 @@ static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
        return 0;
 }
 
+static int check_all_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+       int i;
+       if (is_in_dac_nids(spec, nid))
+               return 1;
+       for (i = 0; i < spec->autocfg.hp_outs; i++)
+               if (spec->hp_dacs[i] == nid)
+                       return 1;
+       for (i = 0; i < spec->autocfg.speaker_outs; i++)
+               if (spec->speaker_dacs[i] == nid)
+                       return 1;
+       return 0;
+}
+
+static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       int j, conn_len;
+       hda_nid_t conn[HDA_MAX_CONNECTIONS];
+       unsigned int wcaps, wtype;
+
+       conn_len = snd_hda_get_connections(codec, nid, conn,
+                                          HDA_MAX_CONNECTIONS);
+       for (j = 0; j < conn_len; j++) {
+               wcaps = snd_hda_param_read(codec, conn[j],
+                                          AC_PAR_AUDIO_WIDGET_CAP);
+               wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+               /* we check only analog outputs */
+               if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL))
+                       continue;
+               /* if this route has a free DAC, assign it */
+               if (!check_all_dac_nids(spec, conn[j])) {
+                       if (conn_len > 1) {
+                               /* select this DAC in the pin's input mux */
+                               snd_hda_codec_write_cache(codec, nid, 0,
+                                                 AC_VERB_SET_CONNECT_SEL, j);
+                       }
+                       return conn[j];
+               }
+       }
+       return 0;
+}
+
+static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid);
+static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid);
+
 /*
  * Fill in the dac_nids table from the parsed pin configuration
  * This function only works when every pin in line_out_pins[]
@@ -2757,31 +2825,17 @@ static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
  * codecs are not connected directly to a DAC, such as the 9200
  * and 9202/925x. For those, dac_nids[] must be hard-coded.
  */
-static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
-                                      struct auto_pin_cfg *cfg)
+static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
-       int i, j, conn_len = 0; 
-       hda_nid_t nid, conn[HDA_MAX_CONNECTIONS];
-       unsigned int wcaps, wtype;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i;
+       hda_nid_t nid, dac;
        
        for (i = 0; i < cfg->line_outs; i++) {
                nid = cfg->line_out_pins[i];
-               conn_len = snd_hda_get_connections(codec, nid, conn,
-                                                  HDA_MAX_CONNECTIONS);
-               for (j = 0; j < conn_len; j++) {
-                       wcaps = snd_hda_param_read(codec, conn[j],
-                                                  AC_PAR_AUDIO_WIDGET_CAP);
-                       wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
-                       if (wtype != AC_WID_AUD_OUT ||
-                           (wcaps & AC_WCAP_DIGITAL))
-                               continue;
-                       /* conn[j] is a DAC routed to this line-out */
-                       if (!is_in_dac_nids(spec, conn[j]))
-                               break;
-               }
-
-               if (j == conn_len) {
+               dac = get_unassigned_dac(codec, nid);
+               if (!dac) {
                        if (spec->multiout.num_dacs > 0) {
                                /* we have already working output pins,
                                 * so let's drop the broken ones again
@@ -2795,24 +2849,64 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
                                   __func__, nid);
                        return -ENODEV;
                }
+               add_spec_dacs(spec, dac);
+       }
 
-               spec->multiout.dac_nids[i] = conn[j];
-               spec->multiout.num_dacs++;
-               if (conn_len > 1) {
-                       /* select this DAC in the pin's input mux */
-                       snd_hda_codec_write_cache(codec, nid, 0,
-                                                 AC_VERB_SET_CONNECT_SEL, j);
+       /* add line-in as output */
+       nid = check_line_out_switch(codec);
+       if (nid) {
+               dac = get_unassigned_dac(codec, nid);
+               if (dac) {
+                       snd_printdd("STAC: Add line-in 0x%x as output %d\n",
+                                   nid, cfg->line_outs);
+                       cfg->line_out_pins[cfg->line_outs] = nid;
+                       cfg->line_outs++;
+                       spec->line_switch = nid;
+                       add_spec_dacs(spec, dac);
+               }
+       }
+       /* add mic as output */
+       nid = check_mic_out_switch(codec);
+       if (nid) {
+               dac = get_unassigned_dac(codec, nid);
+               if (dac) {
+                       snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
+                                   nid, cfg->line_outs);
+                       cfg->line_out_pins[cfg->line_outs] = nid;
+                       cfg->line_outs++;
+                       spec->mic_switch = nid;
+                       add_spec_dacs(spec, dac);
+               }
+       }
 
+       for (i = 0; i < cfg->hp_outs; i++) {
+               nid = cfg->hp_pins[i];
+               dac = get_unassigned_dac(codec, nid);
+               if (dac) {
+                       if (!spec->multiout.hp_nid)
+                               spec->multiout.hp_nid = dac;
+                       else
+                               add_spec_extra_dacs(spec, dac);
                }
+               spec->hp_dacs[i] = dac;
+       }
+
+       for (i = 0; i < cfg->speaker_outs; i++) {
+               nid = cfg->speaker_pins[i];
+               dac = get_unassigned_dac(codec, nid);
+               if (dac)
+                       add_spec_extra_dacs(spec, dac);
+               spec->speaker_dacs[i] = dac;
        }
 
-       snd_printd("dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+       snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
                   spec->multiout.num_dacs,
                   spec->multiout.dac_nids[0],
                   spec->multiout.dac_nids[1],
                   spec->multiout.dac_nids[2],
                   spec->multiout.dac_nids[3],
                   spec->multiout.dac_nids[4]);
+
        return 0;
 }
 
@@ -2837,9 +2931,7 @@ static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_
 
 static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
 {
-       if (!spec->multiout.hp_nid)
-               spec->multiout.hp_nid = nid;
-       else if (spec->multiout.num_dacs > 4) {
+       if (spec->multiout.num_dacs > 4) {
                printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
                return 1;
        } else {
@@ -2849,35 +2941,47 @@ static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
        return 0;
 }
 
-static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
+static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
 {
-       if (is_in_dac_nids(spec, nid))
-               return 1;
+       int i;
+       for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) {
+               if (!spec->multiout.extra_out_nid[i]) {
+                       spec->multiout.extra_out_nid[i] = nid;
+                       return 0;
+               }
+       }
+       printk(KERN_WARNING "stac92xx: No space for extra DAC 0x%x\n", nid);
+       return 1;
+}
+
+static int is_unique_dac(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+       int i;
+
+       if (spec->autocfg.line_outs != 1)
+               return 0;
        if (spec->multiout.hp_nid == nid)
-               return 1;
-       return 0;
+               return 0;
+       for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++)
+               if (spec->multiout.extra_out_nid[i] == nid)
+                       return 0;
+       return 1;
 }
 
 /* add playback controls from the parsed DAC table */
 static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
                                               const struct auto_pin_cfg *cfg)
 {
+       struct sigmatel_spec *spec = codec->spec;
        static const char *chname[4] = {
                "Front", "Surround", NULL /*CLFE*/, "Side"
        };
        hda_nid_t nid = 0;
        int i, err;
+       unsigned int wid_caps;
 
-       struct sigmatel_spec *spec = codec->spec;
-       unsigned int wid_caps, pincap;
-
-
-       for (i = 0; i < cfg->line_outs && i < spec->multiout.num_dacs; i++) {
-               if (!spec->multiout.dac_nids[i])
-                       continue;
-
+       for (i = 0; i < cfg->line_outs && spec->multiout.dac_nids[i]; i++) {
                nid = spec->multiout.dac_nids[i];
-
                if (i == 2) {
                        /* Center/LFE */
                        err = create_controls(spec, "Center", nid, 1);
@@ -2899,16 +3003,24 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
                        }
 
                } else {
-                       err = create_controls(spec, chname[i], nid, 3);
+                       const char *name = chname[i];
+                       /* if it's a single DAC, assign a better name */
+                       if (!i && is_unique_dac(spec, nid)) {
+                               switch (cfg->line_out_type) {
+                               case AUTO_PIN_HP_OUT:
+                                       name = "Headphone";
+                                       break;
+                               case AUTO_PIN_SPEAKER_OUT:
+                                       name = "Speaker";
+                                       break;
+                               }
+                       }
+                       err = create_controls(spec, name, nid, 3);
                        if (err < 0)
                                return err;
                }
        }
 
-       if ((spec->multiout.num_dacs - cfg->line_outs) > 0 &&
-           cfg->hp_outs == 1 && !spec->multiout.hp_nid)
-               spec->multiout.hp_nid = nid;
-
        if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) {
                err = stac92xx_add_control(spec,
                        STAC_CTL_WIDGET_HP_SWITCH,
@@ -2919,45 +3031,19 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
        }
 
        if (spec->line_switch) {
-               nid = cfg->input_pins[AUTO_PIN_LINE];
-               pincap = snd_hda_param_read(codec, nid,
-                                               AC_PAR_PIN_CAP);
-               if (pincap & AC_PINCAP_OUT) {
-                       err = stac92xx_add_control(spec,
-                               STAC_CTL_WIDGET_IO_SWITCH,
-                               "Line In as Output Switch", nid << 8);
-                       if (err < 0)
-                               return err;
-               }
+               err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
+                                          "Line In as Output Switch",
+                                          spec->line_switch << 8);
+               if (err < 0)
+                       return err;
        }
 
        if (spec->mic_switch) {
-               unsigned int def_conf;
-               unsigned int mic_pin = AUTO_PIN_MIC;
-again:
-               nid = cfg->input_pins[mic_pin];
-               def_conf = snd_hda_codec_read(codec, nid, 0,
-                                               AC_VERB_GET_CONFIG_DEFAULT, 0);
-               /* some laptops have an internal analog microphone
-                * which can't be used as a output */
-               if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
-                       pincap = snd_hda_param_read(codec, nid,
-                                                       AC_PAR_PIN_CAP);
-                       if (pincap & AC_PINCAP_OUT) {
-                               err = stac92xx_add_control(spec,
-                                       STAC_CTL_WIDGET_IO_SWITCH,
-                                       "Mic as Output Switch", (nid << 8) | 1);
-                               nid = snd_hda_codec_read(codec, nid, 0,
-                                        AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
-                               if (!check_in_dac_nids(spec, nid))
-                                       add_spec_dacs(spec, nid);
-                               if (err < 0)
-                                       return err;
-                       }
-               } else if (mic_pin == AUTO_PIN_MIC) {
-                       mic_pin = AUTO_PIN_FRONT_MIC;
-                       goto again;
-               }
+               err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
+                                          "Mic as Output Switch",
+                                          (spec->mic_switch << 8) | 1);
+               if (err < 0)
+                       return err;
        }
 
        return 0;
@@ -2969,55 +3055,39 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
 {
        struct sigmatel_spec *spec = codec->spec;
        hda_nid_t nid;
-       int i, old_num_dacs, err;
+       int i, err, nums;
 
-       old_num_dacs = spec->multiout.num_dacs;
+       nums = 0;
        for (i = 0; i < cfg->hp_outs; i++) {
+               static const char *pfxs[] = {
+                       "Headphone", "Headphone2", "Headphone3",
+               };
                unsigned int wid_caps = get_wcaps(codec, cfg->hp_pins[i]);
                if (wid_caps & AC_WCAP_UNSOL_CAP)
                        spec->hp_detect = 1;
-               nid = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
-                                        AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
-               if (check_in_dac_nids(spec, nid))
-                       nid = 0;
-               if (! nid)
+               if (nums >= ARRAY_SIZE(pfxs))
                        continue;
-               add_spec_dacs(spec, nid);
-       }
-       for (i = 0; i < cfg->speaker_outs; i++) {
-               nid = snd_hda_codec_read(codec, cfg->speaker_pins[i], 0,
-                                        AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
-               if (check_in_dac_nids(spec, nid))
-                       nid = 0;
-               if (! nid)
+               nid = spec->hp_dacs[i];
+               if (!nid)
                        continue;
-               add_spec_dacs(spec, nid);
-       }
-       for (i = 0; i < cfg->line_outs; i++) {
-               nid = snd_hda_codec_read(codec, cfg->line_out_pins[i], 0,
-                                       AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
-               if (check_in_dac_nids(spec, nid))
-                       nid = 0;
-               if (! nid)
-                       continue;
-               add_spec_dacs(spec, nid);
+               err = create_controls(spec, pfxs[nums++], nid, 3);
+               if (err < 0)
+                       return err;
        }
-       for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) {
+       nums = 0;
+       for (i = 0; i < cfg->speaker_outs; i++) {
                static const char *pfxs[] = {
                        "Speaker", "External Speaker", "Speaker2",
                };
-               err = create_controls(spec, pfxs[i - old_num_dacs],
-                                     spec->multiout.dac_nids[i], 3);
-               if (err < 0)
-                       return err;
-       }
-       if (spec->multiout.hp_nid) {
-               err = create_controls(spec, "Headphone",
-                                     spec->multiout.hp_nid, 3);
+               if (nums >= ARRAY_SIZE(pfxs))
+                       continue;
+               nid = spec->speaker_dacs[i];
+               if (!nid)
+                       continue;
+               err = create_controls(spec, pfxs[nums++], nid, 3);
                if (err < 0)
                        return err;
        }
-
        return 0;
 }
 
@@ -3355,7 +3425,6 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
 {
        struct sigmatel_spec *spec = codec->spec;
        int err;
-       int hp_speaker_swap = 0;
 
        if ((err = snd_hda_parse_pin_def_config(codec,
                                                &spec->autocfg,
@@ -3373,13 +3442,15 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
                 * speaker_outs so that the following routines can handle
                 * HP pins as primary outputs.
                 */
+               snd_printdd("stac92xx: Enabling multi-HPs workaround\n");
                memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins,
                       sizeof(spec->autocfg.line_out_pins));
                spec->autocfg.speaker_outs = spec->autocfg.line_outs;
                memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins,
                       sizeof(spec->autocfg.hp_pins));
                spec->autocfg.line_outs = spec->autocfg.hp_outs;
-               hp_speaker_swap = 1;
+               spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
+               spec->autocfg.hp_outs = 0;
        }
        if (spec->autocfg.mono_out_pin) {
                int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) &
@@ -3431,11 +3502,11 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
                                         AC_PINCTL_OUT_EN);
        }
 
-       if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0)
-               return err;
-       if (spec->multiout.num_dacs == 0)
-               if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
+       if (!spec->multiout.num_dacs) {
+               err = stac92xx_auto_fill_dac_nids(codec);
+               if (err < 0)
                        return err;
+       }
 
        err = stac92xx_auto_create_multi_out_ctls(codec, &spec->autocfg);
 
@@ -3473,19 +3544,6 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
        }
 #endif
 
-       if (hp_speaker_swap == 1) {
-               /* Restore the hp_outs and line_outs */
-               memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
-                      sizeof(spec->autocfg.line_out_pins));
-               spec->autocfg.hp_outs = spec->autocfg.line_outs;
-               memcpy(spec->autocfg.line_out_pins, spec->autocfg.speaker_pins,
-                      sizeof(spec->autocfg.speaker_pins));
-               spec->autocfg.line_outs = spec->autocfg.speaker_outs;
-               memset(spec->autocfg.speaker_pins, 0,
-                      sizeof(spec->autocfg.speaker_pins));
-               spec->autocfg.speaker_outs = 0;
-       }
-
        err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
 
        if (err < 0)
@@ -3530,11 +3588,12 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
        if (dig_in && spec->autocfg.dig_in_pin)
                spec->dig_in_nid = dig_in;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
        spec->input_mux = &spec->private_imux;
-       spec->dinput_mux = &spec->private_dimux;
+       if (!spec->dinput_mux)
+               spec->dinput_mux = &spec->private_dimux;
        spec->sinput_mux = &spec->private_smux;
        spec->mono_mux = &spec->private_mono_mux;
        spec->amp_mux = &spec->private_amp_mux;
@@ -3638,8 +3697,8 @@ static int stac9200_parse_auto_config(struct hda_codec *codec)
        if (spec->autocfg.dig_in_pin)
                spec->dig_in_nid = 0x04;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
        spec->input_mux = &spec->private_imux;
        spec->dinput_mux = &spec->private_dimux;
@@ -3683,13 +3742,101 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
                           AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
 }
 
+static int stac92xx_add_jack(struct hda_codec *codec,
+               hda_nid_t nid, int type)
+{
+#ifdef CONFIG_SND_JACK
+       struct sigmatel_spec *spec = codec->spec;
+       struct sigmatel_jack *jack;
+       int def_conf = snd_hda_codec_read(codec, nid,
+                       0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+       int connectivity = get_defcfg_connect(def_conf);
+       char name[32];
+
+       if (connectivity && connectivity != AC_JACK_PORT_FIXED)
+               return 0;
+
+       snd_array_init(&spec->jacks, sizeof(*jack), 32);
+       jack = snd_array_new(&spec->jacks);
+       if (!jack)
+               return -ENOMEM;
+       jack->nid = nid;
+       jack->type = type;
+
+       sprintf(name, "%s at %s %s Jack",
+               snd_hda_get_jack_type(def_conf),
+               snd_hda_get_jack_connectivity(def_conf),
+               snd_hda_get_jack_location(def_conf));
+
+       return snd_jack_new(codec->bus->card, name, type, &jack->jack);
+#else
+       return 0;
+#endif
+}
+
+static int stac_add_event(struct sigmatel_spec *spec, hda_nid_t nid,
+                         unsigned char type, int data)
+{
+       struct sigmatel_event *event;
+
+       snd_array_init(&spec->events, sizeof(*event), 32);
+       event = snd_array_new(&spec->events);
+       if (!event)
+               return -ENOMEM;
+       event->nid = nid;
+       event->type = type;
+       event->tag = spec->events.used;
+       event->data = data;
+
+       return event->tag;
+}
+
+static struct sigmatel_event *stac_get_event(struct hda_codec *codec,
+                                            hda_nid_t nid, unsigned char type)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       struct sigmatel_event *event = spec->events.list;
+       int i;
+
+       for (i = 0; i < spec->events.used; i++, event++) {
+               if (event->nid == nid && event->type == type)
+                       return event;
+       }
+       return NULL;
+}
+
+static struct sigmatel_event *stac_get_event_from_tag(struct hda_codec *codec,
+                                                     unsigned char tag)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       struct sigmatel_event *event = spec->events.list;
+       int i;
+
+       for (i = 0; i < spec->events.used; i++, event++) {
+               if (event->tag == tag)
+                       return event;
+       }
+       return NULL;
+}
+
 static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
-                             unsigned int event)
+                             unsigned int type)
 {
-       if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)
-               snd_hda_codec_write_cache(codec, nid, 0,
-                                         AC_VERB_SET_UNSOLICITED_ENABLE,
-                                         (AC_USRSP_EN | event));
+       struct sigmatel_event *event;
+       int tag;
+
+       if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP))
+               return;
+       event = stac_get_event(codec, nid, type);
+       if (event)
+               tag = event->tag;
+       else
+               tag = stac_add_event(codec->spec, nid, type, 0);
+       if (tag < 0)
+               return;
+       snd_hda_codec_write_cache(codec, nid, 0,
+                                 AC_VERB_SET_UNSOLICITED_ENABLE,
+                                 AC_USRSP_EN | tag);
 }
 
 static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
@@ -3709,9 +3856,8 @@ static void stac92xx_power_down(struct hda_codec *codec)
        /* power down inactive DACs */
        hda_nid_t *dac;
        for (dac = spec->dac_list; *dac; dac++)
-               if (!is_in_dac_nids(spec, *dac) &&
-                       spec->multiout.hp_nid != *dac)
-                       snd_hda_codec_write_cache(codec, *dac, 0,
+               if (!check_all_dac_nids(spec, *dac))
+                       snd_hda_codec_write(codec, *dac, 0,
                                        AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
 }
 
@@ -3730,7 +3876,7 @@ static int stac92xx_init(struct hda_codec *codec)
        /* power down adcs initially */
        if (spec->powerdown_adcs)
                for (i = 0; i < spec->num_adcs; i++)
-                       snd_hda_codec_write_cache(codec,
+                       snd_hda_codec_write(codec,
                                spec->adc_nids[i], 0,
                                AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
 
@@ -3746,37 +3892,51 @@ static int stac92xx_init(struct hda_codec *codec)
        /* set up pins */
        if (spec->hp_detect) {
                /* Enable unsolicited responses on the HP widget */
-               for (i = 0; i < cfg->hp_outs; i++)
-                       enable_pin_detect(codec, cfg->hp_pins[i],
-                                         STAC_HP_EVENT);
+               for (i = 0; i < cfg->hp_outs; i++) {
+                       hda_nid_t nid = cfg->hp_pins[i];
+                       enable_pin_detect(codec, nid, STAC_HP_EVENT);
+               }
                /* force to enable the first line-out; the others are set up
                 * in unsol_event
                 */
                stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
-                                        AC_PINCTL_OUT_EN);
-               stac92xx_auto_init_hp_out(codec);
+                               AC_PINCTL_OUT_EN);
                /* fake event to set up pins */
-               codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+               stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0],
+                                      STAC_HP_EVENT);
        } else {
                stac92xx_auto_init_multi_out(codec);
                stac92xx_auto_init_hp_out(codec);
+               for (i = 0; i < cfg->hp_outs; i++)
+                       stac_toggle_power_map(codec, cfg->hp_pins[i], 1);
        }
        for (i = 0; i < AUTO_PIN_LAST; i++) {
                hda_nid_t nid = cfg->input_pins[i];
                if (nid) {
-                       unsigned int pinctl;
+                       unsigned int pinctl, conf;
                        if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
                                /* for mic pins, force to initialize */
                                pinctl = stac92xx_get_vref(codec, nid);
+                               pinctl |= AC_PINCTL_IN_EN;
+                               stac92xx_auto_set_pinctl(codec, nid, pinctl);
                        } else {
                                pinctl = snd_hda_codec_read(codec, nid, 0,
                                        AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
                                /* if PINCTL already set then skip */
-                               if (pinctl & AC_PINCTL_IN_EN)
-                                       continue;
+                               if (!(pinctl & AC_PINCTL_IN_EN)) {
+                                       pinctl |= AC_PINCTL_IN_EN;
+                                       stac92xx_auto_set_pinctl(codec, nid,
+                                                                pinctl);
+                               }
+                       }
+                       conf = snd_hda_codec_read(codec, nid, 0,
+                                             AC_VERB_GET_CONFIG_DEFAULT, 0);
+                       if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
+                               enable_pin_detect(codec, nid,
+                                                 STAC_INSERT_EVENT);
+                               stac_issue_unsol_event(codec, nid,
+                                                      STAC_INSERT_EVENT);
                        }
-                       pinctl |= AC_PINCTL_IN_EN;
-                       stac92xx_auto_set_pinctl(codec, nid, pinctl);
                }
        }
        for (i = 0; i < spec->num_dmics; i++)
@@ -3791,9 +3951,14 @@ static int stac92xx_init(struct hda_codec *codec)
        for (i = 0; i < spec->num_pwrs; i++)  {
                hda_nid_t nid = spec->pwr_nids[i];
                int pinctl, def_conf;
-               int event = STAC_PWR_EVENT;
 
-               if (is_nid_hp_pin(cfg, nid) && spec->hp_detect)
+               /* power on when no jack detection is available */
+               if (!spec->hp_detect) {
+                       stac_toggle_power_map(codec, nid, 1);
+                       continue;
+               }
+
+               if (is_nid_hp_pin(cfg, nid))
                        continue; /* already has an unsol event */
 
                pinctl = snd_hda_codec_read(codec, nid, 0,
@@ -3802,8 +3967,10 @@ static int stac92xx_init(struct hda_codec *codec)
                 * any attempts on powering down a input port cause the
                 * referenced VREF to act quirky.
                 */
-               if (pinctl & AC_PINCTL_IN_EN)
+               if (pinctl & AC_PINCTL_IN_EN) {
+                       stac_toggle_power_map(codec, nid, 1);
                        continue;
+               }
                def_conf = snd_hda_codec_read(codec, nid, 0,
                                              AC_VERB_GET_CONFIG_DEFAULT, 0);
                def_conf = get_defcfg_connect(def_conf);
@@ -3814,30 +3981,54 @@ static int stac92xx_init(struct hda_codec *codec)
                                stac_toggle_power_map(codec, nid, 1);
                        continue;
                }
-               enable_pin_detect(codec, spec->pwr_nids[i], event | i);
-               codec->patch_ops.unsol_event(codec, (event | i) << 26);
+               if (!stac_get_event(codec, nid, STAC_INSERT_EVENT)) {
+                       enable_pin_detect(codec, nid, STAC_PWR_EVENT);
+                       stac_issue_unsol_event(codec, nid, STAC_PWR_EVENT);
+               }
        }
        if (spec->dac_list)
                stac92xx_power_down(codec);
        return 0;
 }
 
+static void stac92xx_free_jacks(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_JACK
+       /* free jack instances manually when clearing/reconfiguring */
+       struct sigmatel_spec *spec = codec->spec;
+       if (!codec->bus->shutdown && spec->jacks.list) {
+               struct sigmatel_jack *jacks = spec->jacks.list;
+               int i;
+               for (i = 0; i < spec->jacks.used; i++)
+                       snd_device_free(codec->bus->card, &jacks[i].jack);
+       }
+       snd_array_free(&spec->jacks);
+#endif
+}
+
+static void stac92xx_free_kctls(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+
+       if (spec->kctls.list) {
+               struct snd_kcontrol_new *kctl = spec->kctls.list;
+               int i;
+               for (i = 0; i < spec->kctls.used; i++)
+                       kfree(kctl[i].name);
+       }
+       snd_array_free(&spec->kctls);
+}
+
 static void stac92xx_free(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
-       int i;
 
        if (! spec)
                return;
 
-       if (spec->kctl_alloc) {
-               for (i = 0; i < spec->num_kctl_used; i++)
-                       kfree(spec->kctl_alloc[i].name);
-               kfree(spec->kctl_alloc);
-       }
-
-       if (spec->bios_pin_configs)
-               kfree(spec->bios_pin_configs);
+       kfree(spec->pin_configs);
+       stac92xx_free_jacks(codec);
+       snd_array_free(&spec->events);
 
        kfree(spec);
        snd_hda_detach_beep_device(codec);
@@ -3856,11 +4047,7 @@ static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
                 * "xxx as Output" mixer switch
                 */
                struct sigmatel_spec *spec = codec->spec;
-               struct auto_pin_cfg *cfg = &spec->autocfg;
-               if ((nid == cfg->input_pins[AUTO_PIN_LINE] &&
-                    spec->line_switch) ||
-                   (nid == cfg->input_pins[AUTO_PIN_MIC] &&
-                    spec->mic_switch))
+               if (nid == spec->line_switch || nid == spec->mic_switch)
                        return;
        }
 
@@ -3884,20 +4071,13 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
                        pin_ctl & ~flag);
 }
 
-static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid)
+static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
 {
        if (!nid)
                return 0;
        if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
-           & (1 << 31)) {
-               unsigned int pinctl;
-               pinctl = snd_hda_codec_read(codec, nid, 0,
-                                           AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-               if (pinctl & AC_PINCTL_IN_EN)
-                       return 0; /* mic- or line-input */
-               else
-                       return 1; /* HP-output */
-       }
+           & (1 << 31))
+               return 1;
        return 0;
 }
 
@@ -3909,11 +4089,9 @@ static int no_hp_sensing(struct sigmatel_spec *spec, int i)
        struct auto_pin_cfg *cfg = &spec->autocfg;
 
        /* ignore sensing of shared line and mic jacks */
-       if (spec->line_switch &&
-           cfg->hp_pins[i] == cfg->input_pins[AUTO_PIN_LINE])
+       if (cfg->hp_pins[i] == spec->line_switch)
                return 1;
-       if (spec->mic_switch &&
-           cfg->hp_pins[i] == cfg->input_pins[AUTO_PIN_MIC])
+       if (cfg->hp_pins[i] == spec->mic_switch)
                return 1;
        /* ignore if the pin is set as line-out */
        if (cfg->hp_pins[i] == spec->hp_switch)
@@ -3921,7 +4099,7 @@ static int no_hp_sensing(struct sigmatel_spec *spec, int i)
        return 0;
 }
 
-static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
+static void stac92xx_hp_detect(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
        struct auto_pin_cfg *cfg = &spec->autocfg;
@@ -3937,7 +4115,14 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
                        break;
                if (no_hp_sensing(spec, i))
                        continue;
-               presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
+               presence = get_pin_presence(codec, cfg->hp_pins[i]);
+               if (presence) {
+                       unsigned int pinctl;
+                       pinctl = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
+                                           AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+                       if (pinctl & AC_PINCTL_IN_EN)
+                               presence = 0; /* mic- or line-input */
+               }
        }
 
        if (presence) {
@@ -4014,50 +4199,145 @@ static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
 
 static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid)
 {
-       stac_toggle_power_map(codec, nid, get_hp_pin_presence(codec, nid));
+       stac_toggle_power_map(codec, nid, get_pin_presence(codec, nid));
+}
+
+static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       struct sigmatel_jack *jacks = spec->jacks.list;
+
+       if (jacks) {
+               int i;
+               for (i = 0; i < spec->jacks.used; i++) {
+                       if (jacks->nid == nid) {
+                               unsigned int pin_ctl =
+                                       snd_hda_codec_read(codec, nid,
+                                       0, AC_VERB_GET_PIN_WIDGET_CONTROL,
+                                        0x00);
+                               int type = jacks->type;
+                               if (type == (SND_JACK_LINEOUT
+                                               | SND_JACK_HEADPHONE))
+                                       type = (pin_ctl & AC_PINCTL_HP_EN)
+                                       ? SND_JACK_HEADPHONE : SND_JACK_LINEOUT;
+                               snd_jack_report(jacks->jack,
+                                       get_pin_presence(codec, nid)
+                                       ? type : 0);
+                       }
+                       jacks++;
+               }
+       }
+}
+
+static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid,
+                                  unsigned char type)
+{
+       struct sigmatel_event *event = stac_get_event(codec, nid, type);
+       if (!event)
+               return;
+       codec->patch_ops.unsol_event(codec, (unsigned)event->tag << 26);
 }
 
 static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
 {
        struct sigmatel_spec *spec = codec->spec;
-       int idx = res >> 26 & 0x0f;
+       struct sigmatel_event *event;
+       int tag, data;
 
-       switch ((res >> 26) & 0x70) {
+       tag = (res >> 26) & 0x7f;
+       event = stac_get_event_from_tag(codec, tag);
+       if (!event)
+               return;
+
+       switch (event->type) {
        case STAC_HP_EVENT:
-               stac92xx_hp_detect(codec, res);
+               stac92xx_hp_detect(codec);
                /* fallthru */
+       case STAC_INSERT_EVENT:
        case STAC_PWR_EVENT:
                if (spec->num_pwrs > 0)
-                       stac92xx_pin_sense(codec, idx);
+                       stac92xx_pin_sense(codec, event->nid);
+               stac92xx_report_jack(codec, event->nid);
                break;
-       case STAC_VREF_EVENT: {
-               int data = snd_hda_codec_read(codec, codec->afg, 0,
-                       AC_VERB_GET_GPIO_DATA, 0);
+       case STAC_VREF_EVENT:
+               data = snd_hda_codec_read(codec, codec->afg, 0,
+                                         AC_VERB_GET_GPIO_DATA, 0);
                /* toggle VREF state based on GPIOx status */
                snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
-                       !!(data & (1 << idx)));
+                                   !!(data & (1 << event->data)));
                break;
-               }
        }
 }
 
+#ifdef CONFIG_PROC_FS
+static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
+                              struct hda_codec *codec, hda_nid_t nid)
+{
+       if (nid == codec->afg)
+               snd_iprintf(buffer, "Power-Map: 0x%02x\n", 
+                           snd_hda_codec_read(codec, nid, 0, 0x0fec, 0x0));
+}
+
+static void analog_loop_proc_hook(struct snd_info_buffer *buffer,
+                                 struct hda_codec *codec,
+                                 unsigned int verb)
+{
+       snd_iprintf(buffer, "Analog Loopback: 0x%02x\n",
+                   snd_hda_codec_read(codec, codec->afg, 0, verb, 0));
+}
+
+/* stac92hd71bxx, stac92hd73xx */
+static void stac92hd7x_proc_hook(struct snd_info_buffer *buffer,
+                                struct hda_codec *codec, hda_nid_t nid)
+{
+       stac92hd_proc_hook(buffer, codec, nid);
+       if (nid == codec->afg)
+               analog_loop_proc_hook(buffer, codec, 0xfa0);
+}
+
+static void stac9205_proc_hook(struct snd_info_buffer *buffer,
+                              struct hda_codec *codec, hda_nid_t nid)
+{
+       if (nid == codec->afg)
+               analog_loop_proc_hook(buffer, codec, 0xfe0);
+}
+
+static void stac927x_proc_hook(struct snd_info_buffer *buffer,
+                              struct hda_codec *codec, hda_nid_t nid)
+{
+       if (nid == codec->afg)
+               analog_loop_proc_hook(buffer, codec, 0xfeb);
+}
+#else
+#define stac92hd_proc_hook     NULL
+#define stac92hd7x_proc_hook   NULL
+#define stac9205_proc_hook     NULL
+#define stac927x_proc_hook     NULL
+#endif
+
 #ifdef SND_HDA_NEEDS_RESUME
 static int stac92xx_resume(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
 
        stac92xx_set_config_regs(codec);
-       snd_hda_sequence_write(codec, spec->init);
-       stac_gpio_set(codec, spec->gpio_mask,
-               spec->gpio_dir, spec->gpio_data);
+       stac92xx_init(codec);
        snd_hda_codec_resume_amp(codec);
        snd_hda_codec_resume_cache(codec);
-       /* power down inactive DACs */
-       if (spec->dac_list)
-               stac92xx_power_down(codec);
-       /* invoke unsolicited event to reset the HP state */
+       /* fake event to set up pins again to override cached values */
        if (spec->hp_detect)
-               codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+               stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0],
+                                      STAC_HP_EVENT);
+       return 0;
+}
+
+static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       if (spec->eapd_mask)
+               stac_gpio_set(codec, spec->gpio_mask,
+                               spec->gpio_dir, spec->gpio_data &
+                               ~spec->eapd_mask);
        return 0;
 }
 #endif
@@ -4069,6 +4349,7 @@ static struct hda_codec_ops stac92xx_patch_ops = {
        .free = stac92xx_free,
        .unsol_event = stac92xx_unsol_event,
 #ifdef SND_HDA_NEEDS_RESUME
+       .suspend = stac92xx_suspend,
        .resume = stac92xx_resume,
 #endif
 };
@@ -4091,14 +4372,12 @@ static int patch_stac9200(struct hda_codec *codec)
        if (spec->board_config < 0) {
                snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
-               if (err < 0) {
-                       stac92xx_free(codec);
-                       return err;
-               }
-               spec->pin_configs = spec->bios_pin_configs;
-       } else {
-               spec->pin_configs = stac9200_brd_tbl[spec->board_config];
-               stac92xx_set_config_regs(codec);
+       } else
+               err = stac_save_pin_cfgs(codec,
+                                        stac9200_brd_tbl[spec->board_config]);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
        }
 
        spec->multiout.max_channels = 2;
@@ -4154,14 +4433,12 @@ static int patch_stac925x(struct hda_codec *codec)
                snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x," 
                                      "using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
-               if (err < 0) {
-                       stac92xx_free(codec);
-                       return err;
-               }
-               spec->pin_configs = spec->bios_pin_configs;
-       } else if (stac925x_brd_tbl[spec->board_config] != NULL){
-               spec->pin_configs = stac925x_brd_tbl[spec->board_config];
-               stac92xx_set_config_regs(codec);
+       } else
+               err = stac_save_pin_cfgs(codec,
+                                        stac925x_brd_tbl[spec->board_config]);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
        }
 
        spec->multiout.max_channels = 2;
@@ -4225,6 +4502,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec)
        struct sigmatel_spec *spec;
        hda_nid_t conn[STAC92HD73_DAC_COUNT + 2];
        int err = 0;
+       int num_dacs;
 
        spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -4243,43 +4521,37 @@ again:
                snd_printdd(KERN_INFO "hda_codec: Unknown model for"
                        " STAC92HD73XX, using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
-               if (err < 0) {
-                       stac92xx_free(codec);
-                       return err;
-               }
-               spec->pin_configs = spec->bios_pin_configs;
-       } else {
-               spec->pin_configs = stac92hd73xx_brd_tbl[spec->board_config];
-               stac92xx_set_config_regs(codec);
+       } else
+               err = stac_save_pin_cfgs(codec,
+                               stac92hd73xx_brd_tbl[spec->board_config]);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
        }
 
-       spec->multiout.num_dacs = snd_hda_get_connections(codec, 0x0a,
+       num_dacs = snd_hda_get_connections(codec, 0x0a,
                        conn, STAC92HD73_DAC_COUNT + 2) - 1;
 
-       if (spec->multiout.num_dacs < 0) {
+       if (num_dacs < 3 || num_dacs > 5) {
                printk(KERN_WARNING "hda_codec: Could not determine "
                       "number of channels defaulting to DAC count\n");
-               spec->multiout.num_dacs = STAC92HD73_DAC_COUNT;
+               num_dacs = STAC92HD73_DAC_COUNT;
        }
-
-       switch (spec->multiout.num_dacs) {
+       switch (num_dacs) {
        case 0x3: /* 6 Channel */
-               spec->multiout.hp_nid = 0x17;
                spec->mixer = stac92hd73xx_6ch_mixer;
                spec->init = stac92hd73xx_6ch_core_init;
                break;
        case 0x4: /* 8 Channel */
-               spec->multiout.hp_nid = 0x18;
                spec->mixer = stac92hd73xx_8ch_mixer;
                spec->init = stac92hd73xx_8ch_core_init;
                break;
        case 0x5: /* 10 Channel */
-               spec->multiout.hp_nid = 0x19;
                spec->mixer = stac92hd73xx_10ch_mixer;
                spec->init = stac92hd73xx_10ch_core_init;
-       };
+       }
+       spec->multiout.dac_nids = spec->dac_nids;
 
-       spec->multiout.dac_nids = stac92hd73xx_dac_nids;
        spec->aloopback_mask = 0x01;
        spec->aloopback_shift = 8;
 
@@ -4310,9 +4582,8 @@ again:
                spec->amp_nids = &stac92hd73xx_amp_nids[DELL_M6_AMP];
                spec->eapd_switch = 0;
                spec->num_amps = 1;
-               spec->multiout.hp_nid = 0; /* dual HPs */
 
-               if (!spec->init)
+               if (spec->board_config != STAC_DELL_EQ)
                        spec->init = dell_m6_core_init;
                switch (spec->board_config) {
                case STAC_DELL_M6_AMIC: /* Analog Mics */
@@ -4370,6 +4641,8 @@ again:
 
        codec->patch_ops = stac92xx_patch_ops;
 
+       codec->proc_widget_hook = stac92hd7x_proc_hook;
+
        return 0;
 }
 
@@ -4401,17 +4674,15 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
        spec->pwr_nids = stac92hd83xxx_pwr_nids;
        spec->pwr_mapping = stac92hd83xxx_pwr_mapping;
        spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
-       spec->multiout.dac_nids = stac92hd83xxx_dac_nids;
+       spec->multiout.dac_nids = spec->dac_nids;
 
        spec->init = stac92hd83xxx_core_init;
        switch (codec->vendor_id) {
        case 0x111d7605:
-               spec->multiout.num_dacs = STAC92HD81_DAC_COUNT;
                break;
        default:
                spec->num_pwrs--;
                spec->init++; /* switch to config #2 */
-               spec->multiout.num_dacs = STAC92HD83_DAC_COUNT;
        }
 
        spec->mixer = stac92hd83xxx_mixer;
@@ -4430,14 +4701,12 @@ again:
                snd_printdd(KERN_INFO "hda_codec: Unknown model for"
                        " STAC92HD83XXX, using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
-               if (err < 0) {
-                       stac92xx_free(codec);
-                       return err;
-               }
-               spec->pin_configs = spec->bios_pin_configs;
-       } else {
-               spec->pin_configs = stac92hd83xxx_brd_tbl[spec->board_config];
-               stac92xx_set_config_regs(codec);
+       } else
+               err = stac_save_pin_cfgs(codec,
+                               stac92hd83xxx_brd_tbl[spec->board_config]);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
        }
 
        err = stac92xx_parse_auto_config(codec, 0x1d, 0);
@@ -4458,56 +4727,10 @@ again:
 
        codec->patch_ops = stac92xx_patch_ops;
 
-       return 0;
-}
+       codec->proc_widget_hook = stac92hd_proc_hook;
 
-#ifdef SND_HDA_NEEDS_RESUME
-static void stac92hd71xx_set_power_state(struct hda_codec *codec, int pwr)
-{
-       struct sigmatel_spec *spec = codec->spec;
-       int i;
-       snd_hda_codec_write_cache(codec, codec->afg, 0,
-               AC_VERB_SET_POWER_STATE, pwr);
-
-       msleep(1);
-       for (i = 0; i < spec->num_adcs; i++) {
-               snd_hda_codec_write_cache(codec,
-                       spec->adc_nids[i], 0,
-                       AC_VERB_SET_POWER_STATE, pwr);
-       }
-};
-
-static int stac92hd71xx_resume(struct hda_codec *codec)
-{
-       stac92hd71xx_set_power_state(codec, AC_PWRST_D0);
-       return stac92xx_resume(codec);
-}
-
-static int stac92hd71xx_suspend(struct hda_codec *codec, pm_message_t state)
-{
-       struct sigmatel_spec *spec = codec->spec;
-
-       stac92hd71xx_set_power_state(codec, AC_PWRST_D3);
-       if (spec->eapd_mask)
-               stac_gpio_set(codec, spec->gpio_mask,
-                               spec->gpio_dir, spec->gpio_data &
-                               ~spec->eapd_mask);
        return 0;
-};
-
-#endif
-
-static struct hda_codec_ops stac92hd71bxx_patch_ops = {
-       .build_controls = stac92xx_build_controls,
-       .build_pcms = stac92xx_build_pcms,
-       .init = stac92xx_init,
-       .free = stac92xx_free,
-       .unsol_event = stac92xx_unsol_event,
-#ifdef SND_HDA_NEEDS_RESUME
-       .resume = stac92hd71xx_resume,
-       .suspend = stac92hd71xx_suspend,
-#endif
-};
+}
 
 static struct hda_input_mux stac92hd71bxx_dmux = {
        .num_items = 4,
@@ -4544,14 +4767,12 @@ again:
                snd_printdd(KERN_INFO "hda_codec: Unknown model for"
                        " STAC92HD71BXX, using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
-               if (err < 0) {
-                       stac92xx_free(codec);
-                       return err;
-               }
-               spec->pin_configs = spec->bios_pin_configs;
-       } else {
-               spec->pin_configs = stac92hd71bxx_brd_tbl[spec->board_config];
-               stac92xx_set_config_regs(codec);
+       } else
+               err = stac_save_pin_cfgs(codec,
+                               stac92hd71bxx_brd_tbl[spec->board_config]);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
        }
 
        if (spec->board_config > STAC_92HD71BXX_REF) {
@@ -4574,21 +4795,21 @@ again:
                switch (spec->board_config) {
                case STAC_HP_M4:
                        /* Enable VREF power saving on GPIO1 detect */
+                       err = stac_add_event(spec, codec->afg,
+                                            STAC_VREF_EVENT, 0x02);
+                       if (err < 0)
+                               return err;
                        snd_hda_codec_write_cache(codec, codec->afg, 0,
                                AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
                        snd_hda_codec_write_cache(codec, codec->afg, 0,
-                                       AC_VERB_SET_UNSOLICITED_ENABLE,
-                                       (AC_USRSP_EN | STAC_VREF_EVENT | 0x01));
+                               AC_VERB_SET_UNSOLICITED_ENABLE,
+                               AC_USRSP_EN | err);
                        spec->gpio_mask |= 0x02;
                        break;
                }
                if ((codec->revision_id & 0xf) == 0 ||
-                               (codec->revision_id & 0xf) == 1) {
-#ifdef SND_HDA_NEEDS_RESUME
-                       codec->patch_ops = stac92hd71bxx_patch_ops;
-#endif
+                   (codec->revision_id & 0xf) == 1)
                        spec->stream_delay = 40; /* 40 milliseconds */
-               }
 
                /* no output amps */
                spec->num_pwrs = 0;
@@ -4597,15 +4818,11 @@ again:
 
                /* disable VSW */
                spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF];
-               stac92xx_set_config_reg(codec, 0xf, 0x40f000f0);
+               stac_change_pin_config(codec, 0xf, 0x40f000f0);
                break;
        case 0x111d7603: /* 6 Port with Analog Mixer */
-               if ((codec->revision_id & 0xf) == 1) {
-#ifdef SND_HDA_NEEDS_RESUME
-                       codec->patch_ops = stac92hd71bxx_patch_ops;
-#endif
+               if ((codec->revision_id & 0xf) == 1)
                        spec->stream_delay = 40; /* 40 milliseconds */
-               }
 
                /* no output amps */
                spec->num_pwrs = 0;
@@ -4635,7 +4852,7 @@ again:
        switch (spec->board_config) {
        case STAC_HP_M4:
                /* enable internal microphone */
-               stac92xx_set_config_reg(codec, 0x0e, 0x01813040);
+               stac_change_pin_config(codec, 0x0e, 0x01813040);
                stac92xx_auto_set_pinctl(codec, 0x0e,
                        AC_PINCTL_IN_EN | AC_PINCTL_VREF_80);
                /* fallthru */
@@ -4656,9 +4873,7 @@ again:
                spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
        };
 
-       spec->multiout.num_dacs = 1;
-       spec->multiout.hp_nid = 0x11;
-       spec->multiout.dac_nids = stac92hd71bxx_dac_nids;
+       spec->multiout.dac_nids = spec->dac_nids;
        if (spec->dinput_mux)
                spec->private_dimux.num_items +=
                        spec->num_dmics -
@@ -4680,6 +4895,8 @@ again:
                return err;
        }
 
+       codec->proc_widget_hook = stac92hd7x_proc_hook;
+
        return 0;
 };
 
@@ -4741,14 +4958,12 @@ static int patch_stac922x(struct hda_codec *codec)
                snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
                        "using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
-               if (err < 0) {
-                       stac92xx_free(codec);
-                       return err;
-               }
-               spec->pin_configs = spec->bios_pin_configs;
-       } else if (stac922x_brd_tbl[spec->board_config] != NULL) {
-               spec->pin_configs = stac922x_brd_tbl[spec->board_config];
-               stac92xx_set_config_regs(codec);
+       } else
+               err = stac_save_pin_cfgs(codec,
+                               stac922x_brd_tbl[spec->board_config]);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
        }
 
        spec->adc_nids = stac922x_adc_nids;
@@ -4811,14 +5026,12 @@ static int patch_stac927x(struct hda_codec *codec)
                        snd_printdd(KERN_INFO "hda_codec: Unknown model for"
                                    "STAC927x, using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
-               if (err < 0) {
-                       stac92xx_free(codec);
-                       return err;
-               }
-               spec->pin_configs = spec->bios_pin_configs;
-       } else {
-               spec->pin_configs = stac927x_brd_tbl[spec->board_config];
-               stac92xx_set_config_regs(codec);
+       } else
+               err = stac_save_pin_cfgs(codec,
+                               stac927x_brd_tbl[spec->board_config]);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
        }
 
        spec->digbeep_nid = 0x23;
@@ -4848,15 +5061,15 @@ static int patch_stac927x(struct hda_codec *codec)
                case 0x10280209:
                case 0x1028022e:
                        /* correct the device field to SPDIF out */
-                       stac92xx_set_config_reg(codec, 0x21, 0x01442070);
+                       stac_change_pin_config(codec, 0x21, 0x01442070);
                        break;
                };
                /* configure the analog microphone on some laptops */
-               stac92xx_set_config_reg(codec, 0x0c, 0x90a79130);
+               stac_change_pin_config(codec, 0x0c, 0x90a79130);
                /* correct the front output jack as a hp out */
-               stac92xx_set_config_reg(codec, 0x0f, 0x0227011f);
+               stac_change_pin_config(codec, 0x0f, 0x0227011f);
                /* correct the front input jack as a mic */
-               stac92xx_set_config_reg(codec, 0x0e, 0x02a79130);
+               stac_change_pin_config(codec, 0x0e, 0x02a79130);
                /* fallthru */
        case STAC_DELL_3ST:
                /* GPIO2 High = Enable EAPD */
@@ -4904,6 +5117,8 @@ static int patch_stac927x(struct hda_codec *codec)
 
        codec->patch_ops = stac92xx_patch_ops;
 
+       codec->proc_widget_hook = stac927x_proc_hook;
+
        /*
         * !!FIXME!!
         * The STAC927x seem to require fairly long delays for certain
@@ -4942,14 +5157,12 @@ static int patch_stac9205(struct hda_codec *codec)
        if (spec->board_config < 0) {
                snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
-               if (err < 0) {
-                       stac92xx_free(codec);
-                       return err;
-               }
-               spec->pin_configs = spec->bios_pin_configs;
-       } else {
-               spec->pin_configs = stac9205_brd_tbl[spec->board_config];
-               stac92xx_set_config_regs(codec);
+       } else
+               err = stac_save_pin_cfgs(codec,
+                                        stac9205_brd_tbl[spec->board_config]);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
        }
 
        spec->digbeep_nid = 0x23;
@@ -4976,15 +5189,18 @@ static int patch_stac9205(struct hda_codec *codec)
        switch (spec->board_config){
        case STAC_9205_DELL_M43:
                /* Enable SPDIF in/out */
-               stac92xx_set_config_reg(codec, 0x1f, 0x01441030);
-               stac92xx_set_config_reg(codec, 0x20, 0x1c410030);
+               stac_change_pin_config(codec, 0x1f, 0x01441030);
+               stac_change_pin_config(codec, 0x20, 0x1c410030);
 
                /* Enable unsol response for GPIO4/Dock HP connection */
+               err = stac_add_event(spec, codec->afg, STAC_VREF_EVENT, 0x01);
+               if (err < 0)
+                       return err;
                snd_hda_codec_write_cache(codec, codec->afg, 0,
                        AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
                snd_hda_codec_write_cache(codec, codec->afg, 0,
                                          AC_VERB_SET_UNSOLICITED_ENABLE,
-                                         (AC_USRSP_EN | STAC_HP_EVENT));
+                                         AC_USRSP_EN | err);
 
                spec->gpio_dir = 0x0b;
                spec->eapd_mask = 0x01;
@@ -5022,6 +5238,8 @@ static int patch_stac9205(struct hda_codec *codec)
 
        codec->patch_ops = stac92xx_patch_ops;
 
+       codec->proc_widget_hook = stac9205_proc_hook;
+
        return 0;
 }
 
@@ -5078,29 +5296,11 @@ static struct hda_verb vaio_ar_init[] = {
        {}
 };
 
-/* bind volumes of both NID 0x02 and 0x05 */
-static struct hda_bind_ctls vaio_bind_master_vol = {
-       .ops = &snd_hda_bind_vol,
-       .values = {
-               HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
-               HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
-               0
-       },
-};
-
-/* bind volumes of both NID 0x02 and 0x05 */
-static struct hda_bind_ctls vaio_bind_master_sw = {
-       .ops = &snd_hda_bind_sw,
-       .values = {
-               HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
-               HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
-               0,
-       },
-};
-
 static struct snd_kcontrol_new vaio_mixer[] = {
-       HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
-       HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x02, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x02, 0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x05, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Speaker Playback Switch", 0x05, 0, HDA_OUTPUT),
        /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
        HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
@@ -5116,8 +5316,10 @@ static struct snd_kcontrol_new vaio_mixer[] = {
 };
 
 static struct snd_kcontrol_new vaio_ar_mixer[] = {
-       HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
-       HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x02, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x02, 0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x05, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Speaker Playback Switch", 0x05, 0, HDA_OUTPUT),
        /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
        HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
@@ -5158,7 +5360,7 @@ static int stac9872_vaio_init(struct hda_codec *codec)
 
 static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res)
 {
-       if (get_hp_pin_presence(codec, 0x0a)) {
+       if (get_pin_presence(codec, 0x0a)) {
                stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
                stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
        } else {
@@ -5269,7 +5471,7 @@ static int patch_stac9872(struct hda_codec *codec)
 /*
  * patch entries
  */
-struct hda_codec_preset snd_hda_preset_sigmatel[] = {
+static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
        { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
        { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
        { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
@@ -5333,3 +5535,27 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = {
        { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
        {} /* terminator */
 };
+
+MODULE_ALIAS("snd-hda-codec-id:8384*");
+MODULE_ALIAS("snd-hda-codec-id:111d*");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
+
+static struct hda_codec_preset_list sigmatel_list = {
+       .preset = snd_hda_preset_sigmatel,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_sigmatel_init(void)
+{
+       return snd_hda_add_codec_preset(&sigmatel_list);
+}
+
+static void __exit patch_sigmatel_exit(void)
+{
+       snd_hda_delete_codec_preset(&sigmatel_list);
+}
+
+module_init(patch_sigmatel_init)
+module_exit(patch_sigmatel_exit)
index 63e4871..c761394 100644 (file)
 #include <sound/asoundef.h>
 #include "hda_codec.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 
 /* amp values */
 #define AMP_VAL_IDX_SHIFT      19
 #define AMP_VAL_IDX_MASK       (0x0f<<19)
 
-#define NUM_CONTROL_ALLOC      32
-#define NUM_VERB_ALLOC         32
-
 /* Pin Widget NID */
 #define VT1708_HP_NID          0x13
 #define VT1708_DIGOUT_NID      0x14
@@ -145,8 +141,6 @@ enum {
        AUTO_SEQ_SIDE
 };
 
-#define get_amp_nid(kc)        ((kc)->private_value & 0xffff)
-
 /* Some VT1708S based boards gets the micboost setting wrong, so we have
  * to apply some brute-force and re-write the TLV's by software. */
 static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag,
@@ -227,8 +221,7 @@ struct via_spec {
 
        /* dynamic controls, init_verbs and input_mux */
        struct auto_pin_cfg autocfg;
-       unsigned int num_kctl_alloc, num_kctl_used;
-       struct snd_kcontrol_new *kctl_alloc;
+       struct snd_array kctls;
        struct hda_input_mux private_imux[2];
        hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
 
@@ -272,33 +265,31 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
 {
        struct snd_kcontrol_new *knew;
 
-       if (spec->num_kctl_used >= spec->num_kctl_alloc) {
-               int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
-               /* array + terminator */
-               knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL);
-               if (!knew)
-                       return -ENOMEM;
-               if (spec->kctl_alloc) {
-                       memcpy(knew, spec->kctl_alloc,
-                              sizeof(*knew) * spec->num_kctl_alloc);
-                       kfree(spec->kctl_alloc);
-               }
-               spec->kctl_alloc = knew;
-               spec->num_kctl_alloc = num;
-       }
-
-       knew = &spec->kctl_alloc[spec->num_kctl_used];
+       snd_array_init(&spec->kctls, sizeof(*knew), 32);
+       knew = snd_array_new(&spec->kctls);
+       if (!knew)
+               return -ENOMEM;
        *knew = vt1708_control_templates[type];
        knew->name = kstrdup(name, GFP_KERNEL);
-
        if (!knew->name)
                return -ENOMEM;
        knew->private_value = val;
-       spec->num_kctl_used++;
        return 0;
 }
 
+static void via_free_kctls(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+
+       if (spec->kctls.list) {
+               struct snd_kcontrol_new *kctl = spec->kctls.list;
+               int i;
+               for (i = 0; i < spec->kctls.used; i++)
+                       kfree(kctl[i].name);
+       }
+       snd_array_free(&spec->kctls);
+}
+
 /* create input playback/capture controls for the given pin */
 static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin,
                                const char *ctlname, int idx, int mix_nid)
@@ -896,6 +887,7 @@ static int via_build_controls(struct hda_codec *codec)
                if (err < 0)
                        return err;
        }
+       via_free_kctls(codec); /* no longer needed */
        return 0;
 }
 
@@ -941,17 +933,11 @@ static int via_build_pcms(struct hda_codec *codec)
 static void via_free(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
-       unsigned int i;
 
        if (!spec)
                return;
 
-       if (spec->kctl_alloc) {
-               for (i = 0; i < spec->num_kctl_used; i++)
-                       kfree(spec->kctl_alloc[i].name);
-               kfree(spec->kctl_alloc);
-       }
-
+       via_free_kctls(codec);
        kfree(codec->spec);
 }
 
@@ -1373,8 +1359,8 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
        if (spec->autocfg.dig_in_pin)
                spec->dig_in_nid = VT1708_DIGIN_NID;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
        spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
 
@@ -1846,8 +1832,8 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
        if (spec->autocfg.dig_in_pin)
                spec->dig_in_nid = VT1709_DIGIN_NID;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
        spec->input_mux = &spec->private_imux[0];
 
@@ -2390,8 +2376,8 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
        if (spec->autocfg.dig_in_pin)
                spec->dig_in_nid = VT1708B_DIGIN_NID;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
        spec->input_mux = &spec->private_imux[0];
 
@@ -2855,8 +2841,8 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
 
        spec->extra_dig_out_nid = 0x15;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
        spec->input_mux = &spec->private_imux[0];
 
@@ -3174,8 +3160,8 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
 
        spec->extra_dig_out_nid = 0x1B;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
        spec->input_mux = &spec->private_imux[0];
 
@@ -3262,74 +3248,97 @@ static int patch_vt1702(struct hda_codec *codec)
 /*
  * patch entries
  */
-struct hda_codec_preset snd_hda_preset_via[] = {
-       { .id = 0x11061708, .name = "VIA VT1708", .patch = patch_vt1708},
-       { .id = 0x11061709, .name = "VIA VT1708", .patch = patch_vt1708},
-       { .id = 0x1106170A, .name = "VIA VT1708", .patch = patch_vt1708},
-       { .id = 0x1106170B, .name = "VIA VT1708", .patch = patch_vt1708},
-       { .id = 0x1106E710, .name = "VIA VT1709 10-Ch",
+static struct hda_codec_preset snd_hda_preset_via[] = {
+       { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
+       { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
+       { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
+       { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
+       { .id = 0x1106e710, .name = "VT1709 10-Ch",
          .patch = patch_vt1709_10ch},
-       { .id = 0x1106E711, .name = "VIA VT1709 10-Ch",
+       { .id = 0x1106e711, .name = "VT1709 10-Ch",
          .patch = patch_vt1709_10ch},
-       { .id = 0x1106E712, .name = "VIA VT1709 10-Ch",
+       { .id = 0x1106e712, .name = "VT1709 10-Ch",
          .patch = patch_vt1709_10ch},
-       { .id = 0x1106E713, .name = "VIA VT1709 10-Ch",
+       { .id = 0x1106e713, .name = "VT1709 10-Ch",
          .patch = patch_vt1709_10ch},
-       { .id = 0x1106E714, .name = "VIA VT1709 6-Ch",
+       { .id = 0x1106e714, .name = "VT1709 6-Ch",
          .patch = patch_vt1709_6ch},
-       { .id = 0x1106E715, .name = "VIA VT1709 6-Ch",
+       { .id = 0x1106e715, .name = "VT1709 6-Ch",
          .patch = patch_vt1709_6ch},
-       { .id = 0x1106E716, .name = "VIA VT1709 6-Ch",
+       { .id = 0x1106e716, .name = "VT1709 6-Ch",
          .patch = patch_vt1709_6ch},
-       { .id = 0x1106E717, .name = "VIA VT1709 6-Ch",
+       { .id = 0x1106e717, .name = "VT1709 6-Ch",
          .patch = patch_vt1709_6ch},
-       { .id = 0x1106E720, .name = "VIA VT1708B 8-Ch",
+       { .id = 0x1106e720, .name = "VT1708B 8-Ch",
          .patch = patch_vt1708B_8ch},
-       { .id = 0x1106E721, .name = "VIA VT1708B 8-Ch",
+       { .id = 0x1106e721, .name = "VT1708B 8-Ch",
          .patch = patch_vt1708B_8ch},
-       { .id = 0x1106E722, .name = "VIA VT1708B 8-Ch",
+       { .id = 0x1106e722, .name = "VT1708B 8-Ch",
          .patch = patch_vt1708B_8ch},
-       { .id = 0x1106E723, .name = "VIA VT1708B 8-Ch",
+       { .id = 0x1106e723, .name = "VT1708B 8-Ch",
          .patch = patch_vt1708B_8ch},
-       { .id = 0x1106E724, .name = "VIA VT1708B 4-Ch",
+       { .id = 0x1106e724, .name = "VT1708B 4-Ch",
          .patch = patch_vt1708B_4ch},
-       { .id = 0x1106E725, .name = "VIA VT1708B 4-Ch",
+       { .id = 0x1106e725, .name = "VT1708B 4-Ch",
          .patch = patch_vt1708B_4ch},
-       { .id = 0x1106E726, .name = "VIA VT1708B 4-Ch",
+       { .id = 0x1106e726, .name = "VT1708B 4-Ch",
          .patch = patch_vt1708B_4ch},
-       { .id = 0x1106E727, .name = "VIA VT1708B 4-Ch",
+       { .id = 0x1106e727, .name = "VT1708B 4-Ch",
          .patch = patch_vt1708B_4ch},
-       { .id = 0x11060397, .name = "VIA VT1708S",
+       { .id = 0x11060397, .name = "VT1708S",
          .patch = patch_vt1708S},
-       { .id = 0x11061397, .name = "VIA VT1708S",
+       { .id = 0x11061397, .name = "VT1708S",
          .patch = patch_vt1708S},
-       { .id = 0x11062397, .name = "VIA VT1708S",
+       { .id = 0x11062397, .name = "VT1708S",
          .patch = patch_vt1708S},
-       { .id = 0x11063397, .name = "VIA VT1708S",
+       { .id = 0x11063397, .name = "VT1708S",
          .patch = patch_vt1708S},
-       { .id = 0x11064397, .name = "VIA VT1708S",
+       { .id = 0x11064397, .name = "VT1708S",
          .patch = patch_vt1708S},
-       { .id = 0x11065397, .name = "VIA VT1708S",
+       { .id = 0x11065397, .name = "VT1708S",
          .patch = patch_vt1708S},
-       { .id = 0x11066397, .name = "VIA VT1708S",
+       { .id = 0x11066397, .name = "VT1708S",
          .patch = patch_vt1708S},
-       { .id = 0x11067397, .name = "VIA VT1708S",
+       { .id = 0x11067397, .name = "VT1708S",
          .patch = patch_vt1708S},
-       { .id = 0x11060398, .name = "VIA VT1702",
+       { .id = 0x11060398, .name = "VT1702",
          .patch = patch_vt1702},
-       { .id = 0x11061398, .name = "VIA VT1702",
+       { .id = 0x11061398, .name = "VT1702",
          .patch = patch_vt1702},
-       { .id = 0x11062398, .name = "VIA VT1702",
+       { .id = 0x11062398, .name = "VT1702",
          .patch = patch_vt1702},
-       { .id = 0x11063398, .name = "VIA VT1702",
+       { .id = 0x11063398, .name = "VT1702",
          .patch = patch_vt1702},
-       { .id = 0x11064398, .name = "VIA VT1702",
+       { .id = 0x11064398, .name = "VT1702",
          .patch = patch_vt1702},
-       { .id = 0x11065398, .name = "VIA VT1702",
+       { .id = 0x11065398, .name = "VT1702",
          .patch = patch_vt1702},
-       { .id = 0x11066398, .name = "VIA VT1702",
+       { .id = 0x11066398, .name = "VT1702",
          .patch = patch_vt1702},
-       { .id = 0x11067398, .name = "VIA VT1702",
+       { .id = 0x11067398, .name = "VT1702",
          .patch = patch_vt1702},
        {} /* terminator */
 };
+
+MODULE_ALIAS("snd-hda-codec-id:1106*");
+
+static struct hda_codec_preset_list via_list = {
+       .preset = snd_hda_preset_via,
+       .owner = THIS_MODULE,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("VIA HD-audio codec");
+
+static int __init patch_via_init(void)
+{
+       return snd_hda_add_codec_preset(&via_list);
+}
+
+static void __exit patch_via_exit(void)
+{
+       snd_hda_delete_codec_preset(&via_list);
+}
+
+module_init(patch_via_init)
+module_exit(patch_via_exit)
index 1b3f117..0dfa054 100644 (file)
@@ -382,23 +382,25 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
        unsigned char status_mask =
                VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX | VT1724_IRQ_MTPCM;
        int handled = 0;
-#ifdef CONFIG_SND_DEBUG
        int timeout = 0;
-#endif
 
        while (1) {
                status = inb(ICEREG1724(ice, IRQSTAT));
                status &= status_mask;
                if (status == 0)
                        break;
-#ifdef CONFIG_SND_DEBUG
                if (++timeout > 10) {
-                       printk(KERN_ERR
-                              "ice1724: Too long irq loop, status = 0x%x\n",
-                              status);
+                       status = inb(ICEREG1724(ice, IRQSTAT));
+                       printk(KERN_ERR "ice1724: Too long irq loop, "
+                              "status = 0x%x\n", status);
+                       if (status & VT1724_IRQ_MPU_TX) {
+                               printk(KERN_ERR "ice1724: Disabling MPU_TX\n");
+                               outb(inb(ICEREG1724(ice, IRQMASK)) |
+                                    VT1724_IRQ_MPU_TX,
+                                    ICEREG1724(ice, IRQMASK));
+                       }
                        break;
                }
-#endif
                handled = 1;
                if (status & VT1724_IRQ_MPU_TX) {
                        spin_lock(&ice->reg_lock);
@@ -2351,7 +2353,6 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
 {
        struct snd_ice1712 *ice;
        int err;
-       unsigned char mask;
        static struct snd_device_ops ops = {
                .dev_free =     snd_vt1724_dev_free,
        };
@@ -2412,9 +2413,9 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
                return -EIO;
        }
 
-       /* unmask used interrupts */
-       mask = VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX;
-       outb(mask, ICEREG1724(ice, IRQMASK));
+       /* MPU_RX and TX irq masks are cleared later dynamically */
+       outb(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX , ICEREG1724(ice, IRQMASK));
+
        /* don't handle FIFO overrun/underruns (just yet),
         * since they cause machine lockups
         */
index 9ff3f9e..59bbaf8 100644 (file)
@@ -1670,7 +1670,7 @@ static irqreturn_t snd_m3_interrupt(int irq, void *dev_id)
                return IRQ_NONE;
 
        if (status & HV_INT_PENDING)
-               tasklet_hi_schedule(&chip->hwvol_tq);
+               tasklet_schedule(&chip->hwvol_tq);
 
        /*
         * ack an assp int if its running
index ae7601f..f23a735 100644 (file)
@@ -1010,7 +1010,7 @@ static int __devinit snd_mixart_create(struct mixart_mgr *mgr, struct snd_card *
                .dev_free = snd_mixart_chip_dev_free,
        };
 
-       mgr->chip[idx] = chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
        if (! chip) {
                snd_printk(KERN_ERR "cannot allocate chip\n");
                return -ENOMEM;
@@ -1025,6 +1025,7 @@ static int __devinit snd_mixart_create(struct mixart_mgr *mgr, struct snd_card *
                return err;
        }
 
+       mgr->chip[idx] = chip;
        snd_card_set_dev(card, &mgr->pci->dev);
 
        return 0;
@@ -1377,6 +1378,7 @@ static int __devinit snd_mixart_probe(struct pci_dev *pci,
                sprintf(card->longname, "%s [PCM #%d]", mgr->longname, i);
 
                if ((err = snd_mixart_create(mgr, card, i)) < 0) {
+                       snd_card_free(card);
                        snd_mixart_free(mgr);
                        return err;
                }
index b9a06c2..d3350f3 100644 (file)
@@ -550,7 +550,7 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
                                mgr->msg_fifo[mgr->msg_fifo_writeptr] = msg;
                                mgr->msg_fifo_writeptr++;
                                mgr->msg_fifo_writeptr %= MSG_FIFO_SIZE;
-                               tasklet_hi_schedule(&mgr->msg_taskq);
+                               tasklet_schedule(&mgr->msg_taskq);
                        }
                        spin_unlock(&mgr->msg_lock);
                        break;
index b60f621..de999c6 100644 (file)
@@ -61,6 +61,7 @@ MODULE_PARM_DESC(enable, "enable card");
 enum {
        MODEL_CMEDIA_REF,       /* C-Media's reference design */
        MODEL_MERIDIAN,         /* AuzenTech X-Meridian */
+       MODEL_HALO,             /* HT-Omega Claro halo */
 };
 
 static struct pci_device_id oxygen_ids[] __devinitdata = {
@@ -74,6 +75,7 @@ static struct pci_device_id oxygen_ids[] __devinitdata = {
        { OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
        { OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
        { OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CMEDIA_REF },
+       { OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_HALO },
        { }
 };
 MODULE_DEVICE_TABLE(pci, oxygen_ids);
@@ -301,6 +303,8 @@ static int generic_probe(struct oxygen *chip, unsigned long driver_data)
                                            PLAYBACK_1_TO_SPDIF |
                                            CAPTURE_0_FROM_I2S_2 |
                                            CAPTURE_1_FROM_SPDIF;
+       }
+       if (driver_data == MODEL_MERIDIAN || driver_data == MODEL_HALO) {
                chip->model.misc_flags = OXYGEN_MISC_MIDI;
                chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
        }
index 10473c0..b06128e 100644 (file)
@@ -1,2 +1,2 @@
-snd-pcxhr-objs := pcxhr.o pcxhr_hwdep.o pcxhr_mixer.o pcxhr_core.o
+snd-pcxhr-objs := pcxhr.o pcxhr_hwdep.o pcxhr_mixer.o pcxhr_core.o pcxhr_mix22.o
 obj-$(CONFIG_SND_PCXHR) += snd-pcxhr.o
index 73de6e9..27cf2c2 100644 (file)
 #include "pcxhr_mixer.h"
 #include "pcxhr_hwdep.h"
 #include "pcxhr_core.h"
+#include "pcxhr_mix22.h"
 
 #define DRIVER_NAME "pcxhr"
 
-MODULE_AUTHOR("Markus Bollinger <bollinger@digigram.com>");
+MODULE_AUTHOR("Markus Bollinger <bollinger@digigram.com>, "
+             "Marc Titinger <titinger@digigram.com>");
 MODULE_DESCRIPTION("Digigram " DRIVER_NAME " " PCXHR_DRIVER_VERSION_STRING);
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Digigram," DRIVER_NAME "}}");
 
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;             /* Index 0-MAX */
-static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;              /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;     /* Enable this card */
-static int mono[SNDRV_CARDS];                                  /* capture in mono only */
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;     /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;      /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
+static int mono[SNDRV_CARDS];                          /* capture  mono only */
 
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for Digigram " DRIVER_NAME " soundcard");
@@ -67,18 +69,58 @@ enum {
        PCI_ID_PCX882HR,
        PCI_ID_VX881HR,
        PCI_ID_PCX881HR,
+       PCI_ID_VX882E,
+       PCI_ID_PCX882E,
+       PCI_ID_VX881E,
+       PCI_ID_PCX881E,
+       PCI_ID_VX1222HR,
        PCI_ID_PCX1222HR,
+       PCI_ID_VX1221HR,
        PCI_ID_PCX1221HR,
+       PCI_ID_VX1222E,
+       PCI_ID_PCX1222E,
+       PCI_ID_VX1221E,
+       PCI_ID_PCX1221E,
+       PCI_ID_VX222HR,
+       PCI_ID_VX222E,
+       PCI_ID_PCX22HR,
+       PCI_ID_PCX22E,
+       PCI_ID_VX222HRMIC,
+       PCI_ID_VX222E_MIC,
+       PCI_ID_PCX924HR,
+       PCI_ID_PCX924E,
+       PCI_ID_PCX924HRMIC,
+       PCI_ID_PCX924E_MIC,
        PCI_ID_LAST
 };
 
 static struct pci_device_id pcxhr_ids[] = {
-       { 0x10b5, 0x9656, 0x1369, 0xb001, 0, 0, PCI_ID_VX882HR, },   /* VX882HR */
-       { 0x10b5, 0x9656, 0x1369, 0xb101, 0, 0, PCI_ID_PCX882HR, },  /* PCX882HR */
-       { 0x10b5, 0x9656, 0x1369, 0xb201, 0, 0, PCI_ID_VX881HR, },   /* VX881HR */
-       { 0x10b5, 0x9656, 0x1369, 0xb301, 0, 0, PCI_ID_PCX881HR, },  /* PCX881HR */
-       { 0x10b5, 0x9656, 0x1369, 0xb501, 0, 0, PCI_ID_PCX1222HR, }, /* PCX1222HR */
-       { 0x10b5, 0x9656, 0x1369, 0xb701, 0, 0, PCI_ID_PCX1221HR, }, /* PCX1221HR */
+       { 0x10b5, 0x9656, 0x1369, 0xb001, 0, 0, PCI_ID_VX882HR, },
+       { 0x10b5, 0x9656, 0x1369, 0xb101, 0, 0, PCI_ID_PCX882HR, },
+       { 0x10b5, 0x9656, 0x1369, 0xb201, 0, 0, PCI_ID_VX881HR, },
+       { 0x10b5, 0x9656, 0x1369, 0xb301, 0, 0, PCI_ID_PCX881HR, },
+       { 0x10b5, 0x9056, 0x1369, 0xb021, 0, 0, PCI_ID_VX882E, },
+       { 0x10b5, 0x9056, 0x1369, 0xb121, 0, 0, PCI_ID_PCX882E, },
+       { 0x10b5, 0x9056, 0x1369, 0xb221, 0, 0, PCI_ID_VX881E, },
+       { 0x10b5, 0x9056, 0x1369, 0xb321, 0, 0, PCI_ID_PCX881E, },
+       { 0x10b5, 0x9656, 0x1369, 0xb401, 0, 0, PCI_ID_VX1222HR, },
+       { 0x10b5, 0x9656, 0x1369, 0xb501, 0, 0, PCI_ID_PCX1222HR, },
+       { 0x10b5, 0x9656, 0x1369, 0xb601, 0, 0, PCI_ID_VX1221HR, },
+       { 0x10b5, 0x9656, 0x1369, 0xb701, 0, 0, PCI_ID_PCX1221HR, },
+       { 0x10b5, 0x9056, 0x1369, 0xb421, 0, 0, PCI_ID_VX1222E, },
+       { 0x10b5, 0x9056, 0x1369, 0xb521, 0, 0, PCI_ID_PCX1222E, },
+       { 0x10b5, 0x9056, 0x1369, 0xb621, 0, 0, PCI_ID_VX1221E, },
+       { 0x10b5, 0x9056, 0x1369, 0xb721, 0, 0, PCI_ID_PCX1221E, },
+       { 0x10b5, 0x9056, 0x1369, 0xba01, 0, 0, PCI_ID_VX222HR, },
+       { 0x10b5, 0x9056, 0x1369, 0xba21, 0, 0, PCI_ID_VX222E, },
+       { 0x10b5, 0x9056, 0x1369, 0xbd01, 0, 0, PCI_ID_PCX22HR, },
+       { 0x10b5, 0x9056, 0x1369, 0xbd21, 0, 0, PCI_ID_PCX22E, },
+       { 0x10b5, 0x9056, 0x1369, 0xbc01, 0, 0, PCI_ID_VX222HRMIC, },
+       { 0x10b5, 0x9056, 0x1369, 0xbc21, 0, 0, PCI_ID_VX222E_MIC, },
+       { 0x10b5, 0x9056, 0x1369, 0xbb01, 0, 0, PCI_ID_PCX924HR, },
+       { 0x10b5, 0x9056, 0x1369, 0xbb21, 0, 0, PCI_ID_PCX924E, },
+       { 0x10b5, 0x9056, 0x1369, 0xbf01, 0, 0, PCI_ID_PCX924HRMIC, },
+       { 0x10b5, 0x9056, 0x1369, 0xbf21, 0, 0, PCI_ID_PCX924E_MIC, },
        { 0, }
 };
 
@@ -88,27 +130,55 @@ struct board_parameters {
        char* board_name;
        short playback_chips;
        short capture_chips;
+       short fw_file_set;
        short firmware_num;
 };
 static struct board_parameters pcxhr_board_params[] = {
-[PCI_ID_VX882HR] =     { "VX882HR",   4, 4, 41, },
-[PCI_ID_PCX882HR] =    { "PCX882HR",  4, 4, 41, },
-[PCI_ID_VX881HR] =     { "VX881HR",   4, 4, 41, },
-[PCI_ID_PCX881HR] =    { "PCX881HR",  4, 4, 41, },
-[PCI_ID_PCX1222HR] =   { "PCX1222HR", 6, 1, 42, },
-[PCI_ID_PCX1221HR] =   { "PCX1221HR", 6, 1, 42, },
+[PCI_ID_VX882HR] =      { "VX882HR",      4, 4, 0, 41 },
+[PCI_ID_PCX882HR] =     { "PCX882HR",     4, 4, 0, 41 },
+[PCI_ID_VX881HR] =      { "VX881HR",      4, 4, 0, 41 },
+[PCI_ID_PCX881HR] =     { "PCX881HR",     4, 4, 0, 41 },
+[PCI_ID_VX882E] =       { "VX882e",       4, 4, 1, 41 },
+[PCI_ID_PCX882E] =      { "PCX882e",      4, 4, 1, 41 },
+[PCI_ID_VX881E] =       { "VX881e",       4, 4, 1, 41 },
+[PCI_ID_PCX881E] =      { "PCX881e",      4, 4, 1, 41 },
+[PCI_ID_VX1222HR] =     { "VX1222HR",     6, 1, 2, 42 },
+[PCI_ID_PCX1222HR] =    { "PCX1222HR",    6, 1, 2, 42 },
+[PCI_ID_VX1221HR] =     { "VX1221HR",     6, 1, 2, 42 },
+[PCI_ID_PCX1221HR] =    { "PCX1221HR",    6, 1, 2, 42 },
+[PCI_ID_VX1222E] =      { "VX1222e",      6, 1, 3, 42 },
+[PCI_ID_PCX1222E] =     { "PCX1222e",     6, 1, 3, 42 },
+[PCI_ID_VX1221E] =      { "VX1221e",      6, 1, 3, 42 },
+[PCI_ID_PCX1221E] =     { "PCX1221e",     6, 1, 3, 42 },
+[PCI_ID_VX222HR] =      { "VX222HR",      1, 1, 4, 44 },
+[PCI_ID_VX222E] =       { "VX222e",       1, 1, 4, 44 },
+[PCI_ID_PCX22HR] =      { "PCX22HR",      1, 0, 4, 44 },
+[PCI_ID_PCX22E] =       { "PCX22e",       1, 0, 4, 44 },
+[PCI_ID_VX222HRMIC] =   { "VX222HR-Mic",  1, 1, 5, 44 },
+[PCI_ID_VX222E_MIC] =   { "VX222e-Mic",   1, 1, 5, 44 },
+[PCI_ID_PCX924HR] =     { "PCX924HR",     1, 1, 5, 44 },
+[PCI_ID_PCX924E] =      { "PCX924e",      1, 1, 5, 44 },
+[PCI_ID_PCX924HRMIC] =  { "PCX924HR-Mic", 1, 1, 5, 44 },
+[PCI_ID_PCX924E_MIC] =  { "PCX924e-Mic",  1, 1, 5, 44 },
 };
 
+/* boards without hw AES1 and SRC onboard are all using fw_file_set==4 */
+/* VX222HR, VX222e, PCX22HR and PCX22e */
+#define PCXHR_BOARD_HAS_AES1(x) (x->fw_file_set != 4)
+/* some boards do not support 192kHz on digital AES input plugs */
+#define PCXHR_BOARD_AESIN_NO_192K(x) ((x->capture_chips == 0) || \
+                                     (x->fw_file_set == 0)   || \
+                                     (x->fw_file_set == 2))
 
 static int pcxhr_pll_freq_register(unsigned int freq, unsigned int* pllreg,
                                   unsigned int* realfreq)
 {
        unsigned int reg;
 
-       if (freq < 6900 || freq > 110250)
+       if (freq < 6900 || freq > 110000)
                return -EINVAL;
-       reg = (28224000 * 10) / freq;
-       reg = (reg + 5) / 10;
+       reg = (28224000 * 2) / freq;
+       reg = (reg - 1) / 2;
        if (reg < 0x200)
                *pllreg = reg + 0x800;
        else if (reg < 0x400)
@@ -121,7 +191,7 @@ static int pcxhr_pll_freq_register(unsigned int freq, unsigned int* pllreg,
                reg &= ~3;
        }
        if (realfreq)
-               *realfreq = ((28224000 * 10) / reg + 5) / 10;
+               *realfreq = (28224000 / (reg + 1));
        return 0;
 }
 
@@ -151,11 +221,6 @@ static int pcxhr_pll_freq_register(unsigned int freq, unsigned int* pllreg,
 #define PCXHR_FREQ_AES_3               0x03
 #define PCXHR_FREQ_AES_4               0x0d
 
-#define PCXHR_MODIFY_CLOCK_S_BIT       0x04
-
-#define PCXHR_IRQ_TIMER_FREQ           92000
-#define PCXHR_IRQ_TIMER_PERIOD         48
-
 static int pcxhr_get_clock_reg(struct pcxhr_mgr *mgr, unsigned int rate,
                               unsigned int *reg, unsigned int *freq)
 {
@@ -196,19 +261,32 @@ static int pcxhr_get_clock_reg(struct pcxhr_mgr *mgr, unsigned int rate,
                        err = pcxhr_send_msg(mgr, &rmh);
                        if (err < 0) {
                                snd_printk(KERN_ERR
-                                          "error CMD_ACCESS_IO_WRITE for PLL register : %x!\n",
-                                          err );
+                                          "error CMD_ACCESS_IO_WRITE "
+                                          "for PLL register : %x!\n", err);
                                return err;
                        }
                }
                break;
-       case PCXHR_CLOCK_TYPE_WORD_CLOCK :      val = PCXHR_FREQ_WORD_CLOCK;    break;
-       case PCXHR_CLOCK_TYPE_AES_SYNC :        val = PCXHR_FREQ_SYNC_AES;      break;
-       case PCXHR_CLOCK_TYPE_AES_1 :           val = PCXHR_FREQ_AES_1;         break;
-       case PCXHR_CLOCK_TYPE_AES_2 :           val = PCXHR_FREQ_AES_2;         break;
-       case PCXHR_CLOCK_TYPE_AES_3 :           val = PCXHR_FREQ_AES_3;         break;
-       case PCXHR_CLOCK_TYPE_AES_4 :           val = PCXHR_FREQ_AES_4;         break;
-       default : return -EINVAL;
+       case PCXHR_CLOCK_TYPE_WORD_CLOCK:
+               val = PCXHR_FREQ_WORD_CLOCK;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_SYNC:
+               val = PCXHR_FREQ_SYNC_AES;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_1:
+               val = PCXHR_FREQ_AES_1;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_2:
+               val = PCXHR_FREQ_AES_2;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_3:
+               val = PCXHR_FREQ_AES_3;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_4:
+               val = PCXHR_FREQ_AES_4;
+               break;
+       default:
+               return -EINVAL;
        }
        *reg = val;
        *freq = realfreq;
@@ -216,14 +294,13 @@ static int pcxhr_get_clock_reg(struct pcxhr_mgr *mgr, unsigned int rate,
 }
 
 
-int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
+static int pcxhr_sub_set_clock(struct pcxhr_mgr *mgr,
+                              unsigned int rate,
+                              int *changed)
 {
        unsigned int val, realfreq, speed;
        struct pcxhr_rmh rmh;
-       int err, changed;
-
-       if (rate == 0)
-               return 0; /* nothing to do */
+       int err;
 
        err = pcxhr_get_clock_reg(mgr, rate, &val, &realfreq);
        if (err)
@@ -237,13 +314,17 @@ int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
        else
                speed = 2;      /* quad speed */
        if (mgr->codec_speed != speed) {
-               pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);      /* mute outputs */
+               pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE); /* mute outputs */
                rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
+               if (DSP_EXT_CMD_SET(mgr)) {
+                       rmh.cmd[1]  = 1;
+                       rmh.cmd_len = 2;
+               }
                err = pcxhr_send_msg(mgr, &rmh);
                if (err)
                        return err;
 
-               pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);      /* set speed ratio */
+               pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE); /* set speed ratio */
                rmh.cmd[0] |= IO_NUM_SPEED_RATIO;
                rmh.cmd[1] = speed;
                rmh.cmd_len = 2;
@@ -253,25 +334,57 @@ int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
        }
        /* set the new frequency */
        snd_printdd("clock register : set %x\n", val);
-       err = pcxhr_write_io_num_reg_cont(mgr, PCXHR_FREQ_REG_MASK, val, &changed);
+       err = pcxhr_write_io_num_reg_cont(mgr, PCXHR_FREQ_REG_MASK,
+                                         val, changed);
        if (err)
                return err;
+
        mgr->sample_rate_real = realfreq;
        mgr->cur_clock_type = mgr->use_clock_type;
 
        /* unmute after codec speed modes */
        if (mgr->codec_speed != speed) {
-               pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);       /* unmute outputs */
+               pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ); /* unmute outputs */
                rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
+               if (DSP_EXT_CMD_SET(mgr)) {
+                       rmh.cmd[1]  = 1;
+                       rmh.cmd_len = 2;
+               }
                err = pcxhr_send_msg(mgr, &rmh);
                if (err)
                        return err;
-               mgr->codec_speed = speed;                       /* save new codec speed */
+               mgr->codec_speed = speed;       /* save new codec speed */
        }
 
+       snd_printdd("pcxhr_sub_set_clock to %dHz (realfreq=%d)\n",
+                   rate, realfreq);
+       return 0;
+}
+
+#define PCXHR_MODIFY_CLOCK_S_BIT       0x04
+
+#define PCXHR_IRQ_TIMER_FREQ           92000
+#define PCXHR_IRQ_TIMER_PERIOD         48
+
+int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
+{
+       struct pcxhr_rmh rmh;
+       int err, changed;
+
+       if (rate == 0)
+               return 0; /* nothing to do */
+
+       if (mgr->is_hr_stereo)
+               err = hr222_sub_set_clock(mgr, rate, &changed);
+       else
+               err = pcxhr_sub_set_clock(mgr, rate, &changed);
+
+       if (err)
+               return err;
+
        if (changed) {
                pcxhr_init_rmh(&rmh, CMD_MODIFY_CLOCK);
-               rmh.cmd[0] |= PCXHR_MODIFY_CLOCK_S_BIT;         /* resync fifos  */
+               rmh.cmd[0] |= PCXHR_MODIFY_CLOCK_S_BIT; /* resync fifos  */
                if (rate < PCXHR_IRQ_TIMER_FREQ)
                        rmh.cmd[1] = PCXHR_IRQ_TIMER_PERIOD;
                else
@@ -282,26 +395,39 @@ int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
                if (err)
                        return err;
        }
-       snd_printdd("pcxhr_set_clock to %dHz (realfreq=%d)\n", rate, realfreq);
        return 0;
 }
 
 
-int pcxhr_get_external_clock(struct pcxhr_mgr *mgr, enum pcxhr_clock_type clock_type,
-                            int *sample_rate)
+static int pcxhr_sub_get_external_clock(struct pcxhr_mgr *mgr,
+                                       enum pcxhr_clock_type clock_type,
+                                       int *sample_rate)
 {
        struct pcxhr_rmh rmh;
        unsigned char reg;
        int err, rate;
 
        switch (clock_type) {
-       case PCXHR_CLOCK_TYPE_WORD_CLOCK :      reg = REG_STATUS_WORD_CLOCK;    break;
-       case PCXHR_CLOCK_TYPE_AES_SYNC :        reg = REG_STATUS_AES_SYNC;      break;
-       case PCXHR_CLOCK_TYPE_AES_1 :           reg = REG_STATUS_AES_1;         break;
-       case PCXHR_CLOCK_TYPE_AES_2 :           reg = REG_STATUS_AES_2;         break;
-       case PCXHR_CLOCK_TYPE_AES_3 :           reg = REG_STATUS_AES_3;         break;
-       case PCXHR_CLOCK_TYPE_AES_4 :           reg = REG_STATUS_AES_4;         break;
-       default : return -EINVAL;
+       case PCXHR_CLOCK_TYPE_WORD_CLOCK:
+               reg = REG_STATUS_WORD_CLOCK;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_SYNC:
+               reg = REG_STATUS_AES_SYNC;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_1:
+               reg = REG_STATUS_AES_1;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_2:
+               reg = REG_STATUS_AES_2;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_3:
+               reg = REG_STATUS_AES_3;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_4:
+               reg = REG_STATUS_AES_4;
+               break;
+       default:
+               return -EINVAL;
        }
        pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);
        rmh.cmd_len = 2;
@@ -311,7 +437,7 @@ int pcxhr_get_external_clock(struct pcxhr_mgr *mgr, enum pcxhr_clock_type clock_
                err = pcxhr_send_msg(mgr, &rmh);
                if (err)
                        return err;
-               udelay(100);            /* wait minimum 2 sample_frames at 32kHz ! */
+               udelay(100);    /* wait minimum 2 sample_frames at 32kHz ! */
                mgr->last_reg_stat = reg;
        }
        rmh.cmd[1]  = REG_STATUS_CURRENT;
@@ -336,6 +462,18 @@ int pcxhr_get_external_clock(struct pcxhr_mgr *mgr, enum pcxhr_clock_type clock_
 }
 
 
+int pcxhr_get_external_clock(struct pcxhr_mgr *mgr,
+                            enum pcxhr_clock_type clock_type,
+                            int *sample_rate)
+{
+       if (mgr->is_hr_stereo)
+               return hr222_get_external_clock(mgr, clock_type,
+                                               sample_rate);
+       else
+               return pcxhr_sub_get_external_clock(mgr, clock_type,
+                                                   sample_rate);
+}
+
 /*
  *  start or stop playback/capture substream
  */
@@ -350,7 +488,8 @@ static int pcxhr_set_stream_state(struct pcxhr_stream *stream)
                start = 1;
        else {
                if (stream->status != PCXHR_STREAM_STATUS_SCHEDULE_STOP) {
-                       snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state CANNOT be stopped\n");
+                       snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state "
+                                  "CANNOT be stopped\n");
                        return -EINVAL;
                }
                start = 0;
@@ -359,11 +498,12 @@ static int pcxhr_set_stream_state(struct pcxhr_stream *stream)
                return -EINVAL;
 
        stream->timer_abs_periods = 0;
-       stream->timer_period_frag = 0;            /* reset theoretical stream pos */
+       stream->timer_period_frag = 0;  /* reset theoretical stream pos */
        stream->timer_buf_periods = 0;
        stream->timer_is_synced = 0;
 
-       stream_mask = stream->pipe->is_capture ? 1 : 1<<stream->substream->number;
+       stream_mask =
+         stream->pipe->is_capture ? 1 : 1<<stream->substream->number;
 
        pcxhr_init_rmh(&rmh, start ? CMD_START_STREAM : CMD_STOP_STREAM);
        pcxhr_set_pipe_cmd_params(&rmh, stream->pipe->is_capture,
@@ -373,8 +513,10 @@ static int pcxhr_set_stream_state(struct pcxhr_stream *stream)
 
        err = pcxhr_send_msg(chip->mgr, &rmh);
        if (err)
-               snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state err=%x;\n", err);
-       stream->status = start ? PCXHR_STREAM_STATUS_STARTED : PCXHR_STREAM_STATUS_STOPPED;
+               snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state err=%x;\n",
+                          err);
+       stream->status =
+         start ? PCXHR_STREAM_STATUS_STARTED : PCXHR_STREAM_STATUS_STOPPED;
        return err;
 }
 
@@ -399,13 +541,15 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
                header = HEADER_FMT_BASE_LIN;
                break;
        case SNDRV_PCM_FORMAT_S16_LE:
-               header = HEADER_FMT_BASE_LIN | HEADER_FMT_16BITS | HEADER_FMT_INTEL;
+               header = HEADER_FMT_BASE_LIN |
+                        HEADER_FMT_16BITS | HEADER_FMT_INTEL;
                break;
        case SNDRV_PCM_FORMAT_S16_BE:
                header = HEADER_FMT_BASE_LIN | HEADER_FMT_16BITS;
                break;
        case SNDRV_PCM_FORMAT_S24_3LE:
-               header = HEADER_FMT_BASE_LIN | HEADER_FMT_24BITS | HEADER_FMT_INTEL;
+               header = HEADER_FMT_BASE_LIN |
+                        HEADER_FMT_24BITS | HEADER_FMT_INTEL;
                break;
        case SNDRV_PCM_FORMAT_S24_3BE:
                header = HEADER_FMT_BASE_LIN | HEADER_FMT_24BITS;
@@ -414,7 +558,8 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
                header = HEADER_FMT_BASE_FLOAT | HEADER_FMT_INTEL;
                break;
        default:
-               snd_printk(KERN_ERR "error pcxhr_set_format() : unknown format\n");
+               snd_printk(KERN_ERR
+                          "error pcxhr_set_format() : unknown format\n");
                return -EINVAL;
        }
        chip = snd_pcm_substream_chip(stream->substream);
@@ -432,14 +577,31 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
        is_capture = stream->pipe->is_capture;
        stream_num = is_capture ? 0 : stream->substream->number;
 
-       pcxhr_init_rmh(&rmh, is_capture ? CMD_FORMAT_STREAM_IN : CMD_FORMAT_STREAM_OUT);
-       pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio, stream_num, 0);
-       if (is_capture)
-               rmh.cmd[0] |= 1<<12;
+       pcxhr_init_rmh(&rmh, is_capture ?
+                      CMD_FORMAT_STREAM_IN : CMD_FORMAT_STREAM_OUT);
+       pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio,
+                                 stream_num, 0);
+       if (is_capture) {
+               /* bug with old dsp versions: */
+               /* bit 12 also sets the format of the playback stream */
+               if (DSP_EXT_CMD_SET(chip->mgr))
+                       rmh.cmd[0] |= 1<<10;
+               else
+                       rmh.cmd[0] |= 1<<12;
+       }
        rmh.cmd[1] = 0;
-       rmh.cmd[2] = header >> 8;
-       rmh.cmd[3] = (header & 0xff) << 16;
-       rmh.cmd_len = 4;
+       rmh.cmd_len = 2;
+       if (DSP_EXT_CMD_SET(chip->mgr)) {
+               /* add channels and set bit 19 if channels>2 */
+               rmh.cmd[1] = stream->channels;
+               if (!is_capture) {
+                       /* playback : add channel mask to command */
+                       rmh.cmd[2] = (stream->channels == 1) ? 0x01 : 0x03;
+                       rmh.cmd_len = 3;
+               }
+       }
+       rmh.cmd[rmh.cmd_len++] = header >> 8;
+       rmh.cmd[rmh.cmd_len++] = (header & 0xff) << 16;
        err = pcxhr_send_msg(chip->mgr, &rmh);
        if (err)
                snd_printk(KERN_ERR "ERROR pcxhr_set_format err=%x;\n", err);
@@ -456,30 +618,38 @@ static int pcxhr_update_r_buffer(struct pcxhr_stream *stream)
        is_capture = (subs->stream == SNDRV_PCM_STREAM_CAPTURE);
        stream_num = is_capture ? 0 : subs->number;
 
-       snd_printdd("pcxhr_update_r_buffer(pcm%c%d) : addr(%p) bytes(%zx) subs(%d)\n",
+       snd_printdd("pcxhr_update_r_buffer(pcm%c%d) : "
+                   "addr(%p) bytes(%zx) subs(%d)\n",
                    is_capture ? 'c' : 'p',
                    chip->chip_idx, (void *)(long)subs->runtime->dma_addr,
                    subs->runtime->dma_bytes, subs->number);
 
        pcxhr_init_rmh(&rmh, CMD_UPDATE_R_BUFFERS);
-       pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio, stream_num, 0);
+       pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio,
+                                 stream_num, 0);
 
        /* max buffer size is 2 MByte */
        snd_BUG_ON(subs->runtime->dma_bytes >= 0x200000);
-       rmh.cmd[1] = subs->runtime->dma_bytes * 8;              /* size in bits */
-       rmh.cmd[2] = subs->runtime->dma_addr >> 24;             /* most significant byte */
-       rmh.cmd[2] |= 1<<19;                                    /* this is a circular buffer */
-       rmh.cmd[3] = subs->runtime->dma_addr & MASK_DSP_WORD;   /* least 3 significant bytes */
+       /* size in bits */
+       rmh.cmd[1] = subs->runtime->dma_bytes * 8;
+       /* most significant byte */
+       rmh.cmd[2] = subs->runtime->dma_addr >> 24;
+       /* this is a circular buffer */
+       rmh.cmd[2] |= 1<<19;
+       /* least 3 significant bytes */
+       rmh.cmd[3] = subs->runtime->dma_addr & MASK_DSP_WORD;
        rmh.cmd_len = 4;
        err = pcxhr_send_msg(chip->mgr, &rmh);
        if (err)
-               snd_printk(KERN_ERR "ERROR CMD_UPDATE_R_BUFFERS err=%x;\n", err);
+               snd_printk(KERN_ERR
+                          "ERROR CMD_UPDATE_R_BUFFERS err=%x;\n", err);
        return err;
 }
 
 
 #if 0
-static int pcxhr_pipe_sample_count(struct pcxhr_stream *stream, snd_pcm_uframes_t *sample_count)
+static int pcxhr_pipe_sample_count(struct pcxhr_stream *stream,
+                                  snd_pcm_uframes_t *sample_count)
 {
        struct pcxhr_rmh rmh;
        int err;
@@ -533,8 +703,8 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
                for (j = 0; j < chip->nb_streams_play; j++) {
                        if (pcxhr_stream_scheduled_get_pipe(&chip->playback_stream[j], &pipe)) {
                                playback_mask |= (1 << pipe->first_audio);
-                               break;  /* add only once, as all playback streams of
-                                        * one chip use the same pipe
+                               break;  /* add only once, as all playback
+                                        * streams of one chip use the same pipe
                                         */
                        }
                }
@@ -545,19 +715,21 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
                return;
        }
 
-       snd_printdd("pcxhr_trigger_tasklet : playback_mask=%x capture_mask=%x\n",
+       snd_printdd("pcxhr_trigger_tasklet : "
+                   "playback_mask=%x capture_mask=%x\n",
                    playback_mask, capture_mask);
 
        /* synchronous stop of all the pipes concerned */
        err = pcxhr_set_pipe_state(mgr,  playback_mask, capture_mask, 0);
        if (err) {
                mutex_unlock(&mgr->setup_mutex);
-               snd_printk(KERN_ERR "pcxhr_trigger_tasklet : error stop pipes (P%x C%x)\n",
+               snd_printk(KERN_ERR "pcxhr_trigger_tasklet : "
+                          "error stop pipes (P%x C%x)\n",
                           playback_mask, capture_mask);
                return;
        }
 
-       /* unfortunately the dsp lost format and buffer info with the stop pipe */
+       /* the dsp lost format and buffer info with the stop pipe */
        for (i = 0; i < mgr->num_cards; i++) {
                struct pcxhr_stream *stream;
                chip = mgr->chip[i];
@@ -596,12 +768,15 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
        err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1);
        if (err) {
                mutex_unlock(&mgr->setup_mutex);
-               snd_printk(KERN_ERR "pcxhr_trigger_tasklet : error start pipes (P%x C%x)\n",
+               snd_printk(KERN_ERR "pcxhr_trigger_tasklet : "
+                          "error start pipes (P%x C%x)\n",
                           playback_mask, capture_mask);
                return;
        }
 
-       /* put the streams into the running state now (increment pointer by interrupt) */
+       /* put the streams into the running state now
+        * (increment pointer by interrupt)
+        */
        spin_lock_irqsave(&mgr->lock, flags);
        for ( i =0; i < mgr->num_cards; i++) {
                struct pcxhr_stream *stream;
@@ -615,7 +790,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
                        stream = &chip->playback_stream[j];
                        if (stream->status == PCXHR_STREAM_STATUS_STARTED) {
                                /* playback will already have advanced ! */
-                               stream->timer_period_frag += PCXHR_GRANULARITY;
+                               stream->timer_period_frag += mgr->granularity;
                                stream->status = PCXHR_STREAM_STATUS_RUNNING;
                        }
                }
@@ -653,7 +828,7 @@ static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd)
                                        PCXHR_STREAM_STATUS_SCHEDULE_RUN;
                                snd_pcm_trigger_done(s, subs);
                        }
-                       tasklet_hi_schedule(&chip->mgr->trigger_taskq);
+                       tasklet_schedule(&chip->mgr->trigger_taskq);
                } else {
                        stream = subs->runtime->private_data;
                        snd_printdd("Only one Substream %c %d\n",
@@ -697,12 +872,14 @@ static int pcxhr_hardware_timer(struct pcxhr_mgr *mgr, int start)
 
        pcxhr_init_rmh(&rmh, CMD_SET_TIMER_INTERRUPT);
        if (start) {
-               mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;    /* last dsp time invalid */
-               rmh.cmd[0] |= PCXHR_GRANULARITY;
+               /* last dsp time invalid */
+               mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
+               rmh.cmd[0] |= mgr->granularity;
        }
        err = pcxhr_send_msg(mgr, &rmh);
        if (err < 0)
-               snd_printk(KERN_ERR "error pcxhr_hardware_timer err(%x)\n", err);
+               snd_printk(KERN_ERR "error pcxhr_hardware_timer err(%x)\n",
+                          err);
        return err;
 }
 
@@ -713,38 +890,16 @@ static int pcxhr_prepare(struct snd_pcm_substream *subs)
 {
        struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
        struct pcxhr_mgr *mgr = chip->mgr;
-       /*
-       struct pcxhr_stream *stream = (pcxhr_stream_t*)subs->runtime->private_data;
-       */
        int err = 0;
 
        snd_printdd("pcxhr_prepare : period_size(%lx) periods(%x) buffer_size(%lx)\n",
                    subs->runtime->period_size, subs->runtime->periods,
                    subs->runtime->buffer_size);
 
-       /*
-       if(subs->runtime->period_size <= PCXHR_GRANULARITY) {
-               snd_printk(KERN_ERR "pcxhr_prepare : error period_size too small (%x)\n",
-                          (unsigned int)subs->runtime->period_size);
-               return -EINVAL;
-       }
-       */
-
        mutex_lock(&mgr->setup_mutex);
 
        do {
-               /* if the stream was stopped before, format and buffer were reset */
-               /*
-               if(stream->status == PCXHR_STREAM_STATUS_STOPPED) {
-                       err = pcxhr_set_format(stream);
-                       if(err) break;
-                       err = pcxhr_update_r_buffer(stream);
-                       if(err) break;
-               }
-               */
-
                /* only the first stream can choose the sample rate */
-               /* the further opened streams will be limited to its frequency (see open) */
                /* set the clock only once (first stream) */
                if (mgr->sample_rate != subs->runtime->rate) {
                        err = pcxhr_set_clock(mgr, subs->runtime->rate);
@@ -787,22 +942,9 @@ static int pcxhr_hw_params(struct snd_pcm_substream *subs,
        stream->channels = channels;
        stream->format = format;
 
-       /* set the format to the board */
-       /*
-       err = pcxhr_set_format(stream);
-       if(err) {
-               mutex_unlock(&mgr->setup_mutex);
-               return err;
-       }
-       */
        /* allocate buffer */
        err = snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw));
 
-       /*
-       if (err > 0) {
-               err = pcxhr_update_r_buffer(stream);
-       }
-       */
        mutex_unlock(&mgr->setup_mutex);
 
        return err;
@@ -820,14 +962,18 @@ static int pcxhr_hw_free(struct snd_pcm_substream *subs)
  */
 static struct snd_pcm_hardware pcxhr_caps =
 {
-       .info             = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-                             SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
-                             0 /*SNDRV_PCM_INFO_PAUSE*/),
-       .formats          = ( SNDRV_PCM_FMTBIT_U8 |
-                             SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
-                             SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
-                             SNDRV_PCM_FMTBIT_FLOAT_LE ),
-       .rates            = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000,
+       .info             = (SNDRV_PCM_INFO_MMAP |
+                            SNDRV_PCM_INFO_INTERLEAVED |
+                            SNDRV_PCM_INFO_MMAP_VALID |
+                            SNDRV_PCM_INFO_SYNC_START),
+       .formats          = (SNDRV_PCM_FMTBIT_U8 |
+                            SNDRV_PCM_FMTBIT_S16_LE |
+                            SNDRV_PCM_FMTBIT_S16_BE |
+                            SNDRV_PCM_FMTBIT_S24_3LE |
+                            SNDRV_PCM_FMTBIT_S24_3BE |
+                            SNDRV_PCM_FMTBIT_FLOAT_LE),
+       .rates            = (SNDRV_PCM_RATE_CONTINUOUS |
+                            SNDRV_PCM_RATE_8000_192000),
        .rate_min         = 8000,
        .rate_max         = 192000,
        .channels_min     = 1,
@@ -847,6 +993,7 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
        struct pcxhr_mgr       *mgr = chip->mgr;
        struct snd_pcm_runtime *runtime = subs->runtime;
        struct pcxhr_stream    *stream;
+       int err;
 
        mutex_lock(&mgr->setup_mutex);
 
@@ -874,6 +1021,18 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
                return -EBUSY;
        }
 
+       /* float format support is in some cases buggy on stereo cards */
+       if (mgr->is_hr_stereo)
+               runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_FLOAT_LE;
+
+       /* buffer-size should better be multiple of period-size */
+       err = snd_pcm_hw_constraint_integer(runtime,
+                                           SNDRV_PCM_HW_PARAM_PERIODS);
+       if (err < 0) {
+               mutex_unlock(&mgr->setup_mutex);
+               return err;
+       }
+
        /* if a sample rate is already used or fixed by external clock,
         * the stream cannot change
         */
@@ -889,7 +1048,8 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
                                mutex_unlock(&mgr->setup_mutex);
                                return -EBUSY;
                        }
-                       runtime->hw.rate_min = runtime->hw.rate_max = external_rate;
+                       runtime->hw.rate_min = external_rate;
+                       runtime->hw.rate_max = external_rate;
                }
        }
 
@@ -899,9 +1059,11 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
 
        runtime->private_data = stream;
 
-       snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 4);
-       snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4);
-
+       /* better get a divisor of granularity values (96 or 192) */
+       snd_pcm_hw_constraint_step(runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
+       snd_pcm_hw_constraint_step(runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
        snd_pcm_set_sync(subs);
 
        mgr->ref_count_rate++;
@@ -919,11 +1081,12 @@ static int pcxhr_close(struct snd_pcm_substream *subs)
 
        mutex_lock(&mgr->setup_mutex);
 
-       snd_printdd("pcxhr_close chip%d subs%d\n", chip->chip_idx, subs->number);
+       snd_printdd("pcxhr_close chip%d subs%d\n",
+                   chip->chip_idx, subs->number);
 
        /* sample rate released */
        if (--mgr->ref_count_rate == 0) {
-               mgr->sample_rate = 0;           /* the sample rate is no more locked */
+               mgr->sample_rate = 0;   /* the sample rate is no more locked */
                pcxhr_hardware_timer(mgr, 0);   /* stop the DSP-timer */
        }
 
@@ -1016,7 +1179,8 @@ static int pcxhr_chip_dev_free(struct snd_device *device)
 
 /*
  */
-static int __devinit pcxhr_create(struct pcxhr_mgr *mgr, struct snd_card *card, int idx)
+static int __devinit pcxhr_create(struct pcxhr_mgr *mgr,
+                                 struct snd_card *card, int idx)
 {
        int err;
        struct snd_pcxhr *chip;
@@ -1024,7 +1188,7 @@ static int __devinit pcxhr_create(struct pcxhr_mgr *mgr, struct snd_card *card,
                .dev_free = pcxhr_chip_dev_free,
        };
 
-       mgr->chip[idx] = chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
        if (! chip) {
                snd_printk(KERN_ERR "cannot allocate chip\n");
                return -ENOMEM;
@@ -1040,7 +1204,7 @@ static int __devinit pcxhr_create(struct pcxhr_mgr *mgr, struct snd_card *card,
 
        if (idx < mgr->capture_chips) {
                if (mgr->mono_capture)
-                       chip->nb_streams_capt = 2;      /* 2 mono streams (left+right) */
+                       chip->nb_streams_capt = 2;      /* 2 mono streams */
                else
                        chip->nb_streams_capt = 1;      /* or 1 stereo stream */
        }
@@ -1050,13 +1214,15 @@ static int __devinit pcxhr_create(struct pcxhr_mgr *mgr, struct snd_card *card,
                return err;
        }
 
+       mgr->chip[idx] = chip;
        snd_card_set_dev(card, &mgr->pci->dev);
 
        return 0;
 }
 
 /* proc interface */
-static void pcxhr_proc_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+static void pcxhr_proc_info(struct snd_info_entry *entry,
+                           struct snd_info_buffer *buffer)
 {
        struct snd_pcxhr *chip = entry->private_data;
        struct pcxhr_mgr *mgr = chip->mgr;
@@ -1069,8 +1235,10 @@ static void pcxhr_proc_info(struct snd_info_entry *entry, struct snd_info_buffer
                short ver_maj = (mgr->dsp_version >> 16) & 0xff;
                short ver_min = (mgr->dsp_version >> 8) & 0xff;
                short ver_build = mgr->dsp_version & 0xff;
-               snd_iprintf(buffer, "module version %s\n", PCXHR_DRIVER_VERSION_STRING);
-               snd_iprintf(buffer, "dsp version %d.%d.%d\n", ver_maj, ver_min, ver_build);
+               snd_iprintf(buffer, "module version %s\n",
+                           PCXHR_DRIVER_VERSION_STRING);
+               snd_iprintf(buffer, "dsp version %d.%d.%d\n",
+                           ver_maj, ver_min, ver_build);
                if (mgr->board_has_analog)
                        snd_iprintf(buffer, "analog io available\n");
                else
@@ -1084,18 +1252,22 @@ static void pcxhr_proc_info(struct snd_info_entry *entry, struct snd_info_buffer
                        if (ref > 0) {
                                if (mgr->sample_rate_real != 0 &&
                                    mgr->sample_rate_real != 48000) {
-                                       ref = (ref * 48000) / mgr->sample_rate_real;
-                                       if (mgr->sample_rate_real >= PCXHR_IRQ_TIMER_FREQ)
+                                       ref = (ref * 48000) /
+                                         mgr->sample_rate_real;
+                                       if (mgr->sample_rate_real >=
+                                           PCXHR_IRQ_TIMER_FREQ)
                                                ref *= 2;
                                }
                                cur = 100 - (100 * cur) / ref;
                                snd_iprintf(buffer, "cpu load    %d%%\n", cur);
-                               snd_iprintf(buffer, "buffer pool %d/%d kWords\n",
+                               snd_iprintf(buffer, "buffer pool %d/%d\n",
                                            rmh.stat[2], rmh.stat[3]);
                        }
                }
-               snd_iprintf(buffer, "dma granularity : %d\n", PCXHR_GRANULARITY);
-               snd_iprintf(buffer, "dsp time errors : %d\n", mgr->dsp_time_err);
+               snd_iprintf(buffer, "dma granularity : %d\n",
+                           mgr->granularity);
+               snd_iprintf(buffer, "dsp time errors : %d\n",
+                           mgr->dsp_time_err);
                snd_iprintf(buffer, "dsp async pipe xrun errors : %d\n",
                            mgr->async_err_pipe_xrun);
                snd_iprintf(buffer, "dsp async stream xrun errors : %d\n",
@@ -1110,33 +1282,52 @@ static void pcxhr_proc_info(struct snd_info_entry *entry, struct snd_info_buffer
                rmh.cmd_idx = CMD_LAST_INDEX;
                if( ! pcxhr_send_msg(mgr, &rmh) ) {
                        int i;
+                       if (rmh.stat_len > 8)
+                               rmh.stat_len = 8;
                        for (i = 0; i < rmh.stat_len; i++)
-                               snd_iprintf(buffer, "debug[%02d] = %06x\n", i,  rmh.stat[i]);
+                               snd_iprintf(buffer, "debug[%02d] = %06x\n",
+                                           i,  rmh.stat[i]);
                }
        } else
                snd_iprintf(buffer, "no firmware loaded\n");
        snd_iprintf(buffer, "\n");
 }
-static void pcxhr_proc_sync(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+static void pcxhr_proc_sync(struct snd_info_entry *entry,
+                           struct snd_info_buffer *buffer)
 {
        struct snd_pcxhr *chip = entry->private_data;
        struct pcxhr_mgr *mgr = chip->mgr;
-       static char *texts[7] = {
-               "Internal", "Word", "AES Sync", "AES 1", "AES 2", "AES 3", "AES 4"
+       static const char *textsHR22[3] = {
+               "Internal", "AES Sync", "AES 1"
+       };
+       static const char *textsPCXHR[7] = {
+               "Internal", "Word", "AES Sync",
+               "AES 1", "AES 2", "AES 3", "AES 4"
        };
+       const char **texts;
+       int max_clock;
+       if (mgr->is_hr_stereo) {
+               texts = textsHR22;
+               max_clock = HR22_CLOCK_TYPE_MAX;
+       } else {
+               texts = textsPCXHR;
+               max_clock = PCXHR_CLOCK_TYPE_MAX;
+       }
 
        snd_iprintf(buffer, "\n%s\n", mgr->longname);
-       snd_iprintf(buffer, "Current Sample Clock\t: %s\n", texts[mgr->cur_clock_type]);
-       snd_iprintf(buffer, "Current Sample Rate\t= %d\n", mgr->sample_rate_real);
-
+       snd_iprintf(buffer, "Current Sample Clock\t: %s\n",
+                   texts[mgr->cur_clock_type]);
+       snd_iprintf(buffer, "Current Sample Rate\t= %d\n",
+                   mgr->sample_rate_real);
        /* commands available when embedded DSP is running */
        if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
                int i, err, sample_rate;
-               for (i = PCXHR_CLOCK_TYPE_WORD_CLOCK; i< (3 + mgr->capture_chips); i++) {
+               for (i = 1; i <= max_clock; i++) {
                        err = pcxhr_get_external_clock(mgr, i, &sample_rate);
                        if (err)
                                break;
-                       snd_iprintf(buffer, "%s Clock\t\t= %d\n", texts[i], sample_rate);
+                       snd_iprintf(buffer, "%s Clock\t\t= %d\n",
+                                   texts[i], sample_rate);
                }
        } else
                snd_iprintf(buffer, "no firmware loaded\n");
@@ -1194,7 +1385,8 @@ static int pcxhr_free(struct pcxhr_mgr *mgr)
 /*
  *    probe function - creates the card manager
  */
-static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+static int __devinit pcxhr_probe(struct pci_dev *pci,
+                                const struct pci_device_id *pci_id)
 {
        static int dev;
        struct pcxhr_mgr *mgr;
@@ -1217,7 +1409,8 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
 
        /* check if we can restrict PCI DMA transfers to 32 bits */
        if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0) {
-               snd_printk(KERN_ERR "architecture does not support 32bit PCI busmaster DMA\n");
+               snd_printk(KERN_ERR "architecture does not support "
+                          "32bit PCI busmaster DMA\n");
                pci_disable_device(pci);
                return -ENXIO;
        }
@@ -1234,11 +1427,25 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
                pci_disable_device(pci);
                return -ENODEV;
        }
-       card_name = pcxhr_board_params[pci_id->driver_data].board_name;
-       mgr->playback_chips = pcxhr_board_params[pci_id->driver_data].playback_chips;
-       mgr->capture_chips  = pcxhr_board_params[pci_id->driver_data].capture_chips;
-       mgr->firmware_num  = pcxhr_board_params[pci_id->driver_data].firmware_num;
+       card_name =
+               pcxhr_board_params[pci_id->driver_data].board_name;
+       mgr->playback_chips =
+               pcxhr_board_params[pci_id->driver_data].playback_chips;
+       mgr->capture_chips  =
+               pcxhr_board_params[pci_id->driver_data].capture_chips;
+       mgr->fw_file_set =
+               pcxhr_board_params[pci_id->driver_data].fw_file_set;
+       mgr->firmware_num  =
+               pcxhr_board_params[pci_id->driver_data].firmware_num;
        mgr->mono_capture = mono[dev];
+       mgr->is_hr_stereo = (mgr->playback_chips == 1);
+       mgr->board_has_aes1 = PCXHR_BOARD_HAS_AES1(mgr);
+       mgr->board_aes_in_192k = !PCXHR_BOARD_AESIN_NO_192K(mgr);
+
+       if (mgr->is_hr_stereo)
+               mgr->granularity = PCXHR_GRANULARITY_HR22;
+       else
+               mgr->granularity = PCXHR_GRANULARITY;
 
        /* resource assignment */
        if ((err = pci_request_regions(pci, card_name)) < 0) {
@@ -1261,7 +1468,8 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
        mgr->irq = pci->irq;
 
        sprintf(mgr->shortname, "Digigram %s", card_name);
-       sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, 0x%lx irq %i", mgr->shortname,
+       sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, 0x%lx irq %i",
+               mgr->shortname,
                mgr->port[0], mgr->port[1], mgr->port[2], mgr->irq);
 
        /* ISR spinlock  */
@@ -1272,10 +1480,14 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
        mutex_init(&mgr->setup_mutex);
 
        /* init taslket */
-       tasklet_init(&mgr->msg_taskq, pcxhr_msg_tasklet, (unsigned long) mgr);
-       tasklet_init(&mgr->trigger_taskq, pcxhr_trigger_tasklet, (unsigned long) mgr);
+       tasklet_init(&mgr->msg_taskq, pcxhr_msg_tasklet,
+                    (unsigned long) mgr);
+       tasklet_init(&mgr->trigger_taskq, pcxhr_trigger_tasklet,
+                    (unsigned long) mgr);
+
        mgr->prmh = kmalloc(sizeof(*mgr->prmh) + 
-                           sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS - PCXHR_SIZE_MAX_STATUS),
+                           sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS -
+                                          PCXHR_SIZE_MAX_STATUS),
                            GFP_KERNEL);
        if (! mgr->prmh) {
                pcxhr_free(mgr);
@@ -1296,7 +1508,8 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
                else
                        idx = index[dev] + i;
 
-               snprintf(tmpid, sizeof(tmpid), "%s-%d", id[dev] ? id[dev] : card_name, i);
+               snprintf(tmpid, sizeof(tmpid), "%s-%d",
+                        id[dev] ? id[dev] : card_name, i);
                card = snd_card_new(idx, tmpid, THIS_MODULE, 0);
 
                if (! card) {
@@ -1310,6 +1523,7 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
                sprintf(card->longname, "%s [PCM #%d]", mgr->longname, i);
 
                if ((err = pcxhr_create(mgr, card, i)) < 0) {
+                       snd_card_free(card);
                        pcxhr_free(mgr);
                        return err;
                }
index 6520647..84131a9 100644 (file)
 #include <linux/mutex.h>
 #include <sound/pcm.h>
 
-#define PCXHR_DRIVER_VERSION           0x000804        /* 0.8.4 */
-#define PCXHR_DRIVER_VERSION_STRING    "0.8.4"         /* 0.8.4 */
+#define PCXHR_DRIVER_VERSION           0x000905        /* 0.9.5 */
+#define PCXHR_DRIVER_VERSION_STRING    "0.9.5"         /* 0.9.5 */
 
 
-#define PCXHR_MAX_CARDS                        6
-#define PCXHR_PLAYBACK_STREAMS         4
+#define PCXHR_MAX_CARDS                6
+#define PCXHR_PLAYBACK_STREAMS 4
 
-#define PCXHR_GRANULARITY              96      /* transfer granularity (should be min 96 and multiple of 48) */
-#define PCXHR_GRANULARITY_MIN          96      /* transfer granularity of pipes and the dsp time (MBOX4) */
+#define PCXHR_GRANULARITY      96      /* min 96 and multiple of 48 */
+/* transfer granularity of pipes and the dsp time (MBOX4) */
+#define PCXHR_GRANULARITY_MIN  96
+/* TODO : granularity could be 64 or 128 */
+#define PCXHR_GRANULARITY_HR22 192     /* granularity for stereo cards */
 
 struct snd_pcxhr;
 struct pcxhr_mgr;
@@ -51,6 +54,11 @@ enum pcxhr_clock_type {
        PCXHR_CLOCK_TYPE_AES_2,
        PCXHR_CLOCK_TYPE_AES_3,
        PCXHR_CLOCK_TYPE_AES_4,
+       PCXHR_CLOCK_TYPE_MAX = PCXHR_CLOCK_TYPE_AES_4,
+       HR22_CLOCK_TYPE_INTERNAL = PCXHR_CLOCK_TYPE_INTERNAL,
+       HR22_CLOCK_TYPE_AES_SYNC,
+       HR22_CLOCK_TYPE_AES_1,
+       HR22_CLOCK_TYPE_MAX = HR22_CLOCK_TYPE_AES_1,
 };
 
 struct pcxhr_mgr {
@@ -61,6 +69,8 @@ struct pcxhr_mgr {
 
        int irq;
 
+       int granularity;
+
        /* card access with 1 mem bar and 2 io bar's */
        unsigned long port[3];
 
@@ -83,11 +93,16 @@ struct pcxhr_mgr {
        /* hardware interface */
        unsigned int dsp_loaded;        /* bit flags of loaded dsp indices */
        unsigned int dsp_version;       /* read from embedded once firmware is loaded */
-       int board_has_analog;           /* if 0 the board is digital only */
-       int mono_capture;               /* if 1 the board does mono capture */
-       int playback_chips;             /* 4 or 6 */
-       int capture_chips;              /* 4 or 1 */
-       int firmware_num;               /* 41 or 42 */
+       int playback_chips;
+       int capture_chips;
+       int fw_file_set;
+       int firmware_num;
+       int is_hr_stereo:1;
+       int board_has_aes1:1;   /* if 1 board has AES1 plug and SRC */
+       int board_has_analog:1; /* if 0 the board is digital only */
+       int board_has_mic:1;    /* if 1 the board has microphone input */
+       int board_aes_in_192k:1;/* if 1 the aes input plugs do support 192kHz */
+       int mono_capture:1;     /* if 1 the board does mono capture */
 
        struct snd_dma_buffer hostport;
 
@@ -106,6 +121,9 @@ struct pcxhr_mgr {
        int async_err_stream_xrun;
        int async_err_pipe_xrun;
        int async_err_other_last;
+
+       unsigned char xlx_cfg;          /* copy of PCXHR_XLX_CFG register */
+       unsigned char xlx_selmic;       /* copy of PCXHR_XLX_SELMIC register */
 };
 
 
@@ -155,24 +173,30 @@ struct snd_pcxhr {
 
        struct snd_pcm *pcm;            /* PCM */
 
-       struct pcxhr_pipe playback_pipe;                /* 1 stereo pipe only */
-       struct pcxhr_pipe capture_pipe[2];              /* 1 stereo pipe or 2 mono pipes */
+       struct pcxhr_pipe playback_pipe;        /* 1 stereo pipe only */
+       struct pcxhr_pipe capture_pipe[2];      /* 1 stereo or 2 mono pipes */
 
        struct pcxhr_stream playback_stream[PCXHR_PLAYBACK_STREAMS];
-       struct pcxhr_stream capture_stream[2];  /* 1 stereo stream or 2 mono streams */
+       struct pcxhr_stream capture_stream[2];  /* 1 stereo or 2 mono streams */
        int nb_streams_play;
        int nb_streams_capt;
 
-       int analog_playback_active[2];          /* Mixer : Master Playback active (!mute) */
-       int analog_playback_volume[2];          /* Mixer : Master Playback Volume */
-       int analog_capture_volume[2];           /* Mixer : Master Capture Volume */
-       int digital_playback_active[PCXHR_PLAYBACK_STREAMS][2]; /* Mixer : Digital Playback Active [streams][stereo]*/
-       int digital_playback_volume[PCXHR_PLAYBACK_STREAMS][2]; /* Mixer : Digital Playback Volume [streams][stereo]*/
-       int digital_capture_volume[2];          /* Mixer : Digital Capture Volume [stereo] */
-       int monitoring_active[2];               /* Mixer : Monitoring Active */
-       int monitoring_volume[2];               /* Mixer : Monitoring Volume */
-       int audio_capture_source;               /* Mixer : Audio Capture Source */
-       unsigned char aes_bits[5];              /* Mixer : IEC958_AES bits */
+       int analog_playback_active[2];  /* Mixer : Master Playback !mute */
+       int analog_playback_volume[2];  /* Mixer : Master Playback Volume */
+       int analog_capture_volume[2];   /* Mixer : Master Capture Volume */
+       int digital_playback_active[PCXHR_PLAYBACK_STREAMS][2];
+       int digital_playback_volume[PCXHR_PLAYBACK_STREAMS][2];
+       int digital_capture_volume[2];  /* Mixer : Digital Capture Volume */
+       int monitoring_active[2];       /* Mixer : Monitoring Active */
+       int monitoring_volume[2];       /* Mixer : Monitoring Volume */
+       int audio_capture_source;       /* Mixer : Audio Capture Source */
+       int mic_volume;                 /* used by cards with MIC only */
+       int mic_boost;                  /* used by cards with MIC only */
+       int mic_active;                 /* used by cards with MIC only */
+       int analog_capture_active;      /* used by cards with MIC only */
+       int phantom_power;              /* used by cards with MIC only */
+
+       unsigned char aes_bits[5];      /* Mixer : IEC958_AES bits */
 };
 
 struct pcxhr_hostport
@@ -184,6 +208,8 @@ struct pcxhr_hostport
 /* exported */
 int pcxhr_create_pcm(struct snd_pcxhr *chip);
 int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate);
-int pcxhr_get_external_clock(struct pcxhr_mgr *mgr, enum pcxhr_clock_type clock_type, int *sample_rate);
+int pcxhr_get_external_clock(struct pcxhr_mgr *mgr,
+                            enum pcxhr_clock_type clock_type,
+                            int *sample_rate);
 
 #endif /* __SOUND_PCXHR_H */
index 7143259..833e718 100644 (file)
@@ -132,13 +132,15 @@ static int pcxhr_check_reg_bit(struct pcxhr_mgr *mgr, unsigned int reg,
                *read = PCXHR_INPB(mgr, reg);
                if ((*read & mask) == bit) {
                        if (i > 100)
-                               snd_printdd("ATTENTION! check_reg(%x) loopcount=%d\n",
+                               snd_printdd("ATTENTION! check_reg(%x) "
+                                           "loopcount=%d\n",
                                            reg, i);
                        return 0;
                }
                i++;
        } while (time_after_eq(end_time, jiffies));
-       snd_printk(KERN_ERR "pcxhr_check_reg_bit: timeout, reg=%x, mask=0x%x, val=0x%x\n",
+       snd_printk(KERN_ERR
+                  "pcxhr_check_reg_bit: timeout, reg=%x, mask=0x%x, val=%x\n",
                   reg, mask, *read);
        return -EIO;
 }
@@ -159,18 +161,22 @@ static int pcxhr_check_reg_bit(struct pcxhr_mgr *mgr, unsigned int reg,
 #define PCXHR_IT_TEST_XILINX           (0x0000003C | PCXHR_MASK_IT_HF1 | \
                                         PCXHR_MASK_IT_MANAGE_HF5)
 #define PCXHR_IT_DOWNLOAD_BOOT         (0x0000000C | PCXHR_MASK_IT_HF1 | \
-                                        PCXHR_MASK_IT_MANAGE_HF5 | PCXHR_MASK_IT_WAIT)
+                                        PCXHR_MASK_IT_MANAGE_HF5 | \
+                                        PCXHR_MASK_IT_WAIT)
 #define PCXHR_IT_RESET_BOARD_FUNC      (0x0000000C | PCXHR_MASK_IT_HF0 | \
-                                        PCXHR_MASK_IT_MANAGE_HF5 | PCXHR_MASK_IT_WAIT_EXTRA)
+                                        PCXHR_MASK_IT_MANAGE_HF5 | \
+                                        PCXHR_MASK_IT_WAIT_EXTRA)
 #define PCXHR_IT_DOWNLOAD_DSP          (0x0000000C | \
-                                        PCXHR_MASK_IT_MANAGE_HF5 | PCXHR_MASK_IT_WAIT)
+                                        PCXHR_MASK_IT_MANAGE_HF5 | \
+                                        PCXHR_MASK_IT_WAIT)
 #define PCXHR_IT_DEBUG                 (0x0000005A | PCXHR_MASK_IT_NO_HF0_HF1)
 #define PCXHR_IT_RESET_SEMAPHORE       (0x0000005C | PCXHR_MASK_IT_NO_HF0_HF1)
 #define PCXHR_IT_MESSAGE               (0x00000074 | PCXHR_MASK_IT_NO_HF0_HF1)
 #define PCXHR_IT_RESET_CHK             (0x00000076 | PCXHR_MASK_IT_NO_HF0_HF1)
 #define PCXHR_IT_UPDATE_RBUFFER                (0x00000078 | PCXHR_MASK_IT_NO_HF0_HF1)
 
-static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr, unsigned int itdsp, int atomic)
+static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr,
+                            unsigned int itdsp, int atomic)
 {
        int err;
        unsigned char reg;
@@ -178,17 +184,21 @@ static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr, unsigned int itdsp, int atom
        if (itdsp & PCXHR_MASK_IT_MANAGE_HF5) {
                /* clear hf5 bit */
                PCXHR_OUTPL(mgr, PCXHR_PLX_MBOX0,
-                           PCXHR_INPL(mgr, PCXHR_PLX_MBOX0) & ~PCXHR_MBOX0_HF5);
+                           PCXHR_INPL(mgr, PCXHR_PLX_MBOX0) &
+                           ~PCXHR_MBOX0_HF5);
        }
        if ((itdsp & PCXHR_MASK_IT_NO_HF0_HF1) == 0) {
-               reg = PCXHR_ICR_HI08_RREQ | PCXHR_ICR_HI08_TREQ | PCXHR_ICR_HI08_HDRQ;
+               reg = (PCXHR_ICR_HI08_RREQ |
+                      PCXHR_ICR_HI08_TREQ |
+                      PCXHR_ICR_HI08_HDRQ);
                if (itdsp & PCXHR_MASK_IT_HF0)
                        reg |= PCXHR_ICR_HI08_HF0;
                if (itdsp & PCXHR_MASK_IT_HF1)
                        reg |= PCXHR_ICR_HI08_HF1;
                PCXHR_OUTPB(mgr, PCXHR_DSP_ICR, reg);
        }
-       reg = (unsigned char)(((itdsp & PCXHR_MASK_EXTRA_INFO) >> 1) | PCXHR_CVR_HI08_HC);
+       reg = (unsigned char)(((itdsp & PCXHR_MASK_EXTRA_INFO) >> 1) |
+                             PCXHR_CVR_HI08_HC);
        PCXHR_OUTPB(mgr, PCXHR_DSP_CVR, reg);
        if (itdsp & PCXHR_MASK_IT_WAIT) {
                if (atomic)
@@ -211,10 +221,14 @@ static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr, unsigned int itdsp, int atom
        }
        if (itdsp & PCXHR_MASK_IT_MANAGE_HF5) {
                /* wait for hf5 bit */
-               err = pcxhr_check_reg_bit(mgr, PCXHR_PLX_MBOX0, PCXHR_MBOX0_HF5,
-                                         PCXHR_MBOX0_HF5, PCXHR_TIMEOUT_DSP, &reg);
+               err = pcxhr_check_reg_bit(mgr, PCXHR_PLX_MBOX0,
+                                         PCXHR_MBOX0_HF5,
+                                         PCXHR_MBOX0_HF5,
+                                         PCXHR_TIMEOUT_DSP,
+                                         &reg);
                if (err) {
-                       snd_printk(KERN_ERR "pcxhr_send_it_dsp : TIMEOUT HF5\n");
+                       snd_printk(KERN_ERR
+                                  "pcxhr_send_it_dsp : TIMEOUT HF5\n");
                        return err;
                }
        }
@@ -263,7 +277,8 @@ void pcxhr_enable_dsp(struct pcxhr_mgr *mgr)
 /*
  * load the xilinx image
  */
-int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr, const struct firmware *xilinx, int second)
+int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr,
+                            const struct firmware *xilinx, int second)
 {
        unsigned int i;
        unsigned int chipsc;
@@ -274,7 +289,9 @@ int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr, const struct firmware *xilin
        /* test first xilinx */
        chipsc = PCXHR_INPL(mgr, PCXHR_PLX_CHIPSC);
        /* REV01 cards do not support the PCXHR_CHIPSC_GPI_USERI bit anymore */
-       /* this bit will always be 1; no possibility to test presence of first xilinx */
+       /* this bit will always be 1;
+        * no possibility to test presence of first xilinx
+        */
        if(second) {
                if ((chipsc & PCXHR_CHIPSC_GPI_USERI) == 0) {
                        snd_printk(KERN_ERR "error loading first xilinx\n");
@@ -290,7 +307,8 @@ int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr, const struct firmware *xilin
                data = *image;
                mask = 0x80;
                while (mask) {
-                       chipsc &= ~(PCXHR_CHIPSC_DATA_CLK | PCXHR_CHIPSC_DATA_IN);
+                       chipsc &= ~(PCXHR_CHIPSC_DATA_CLK |
+                                   PCXHR_CHIPSC_DATA_IN);
                        if (data & mask)
                                chipsc |= PCXHR_CHIPSC_DATA_IN;
                        PCXHR_OUTPL(mgr, PCXHR_PLX_CHIPSC, chipsc);
@@ -330,15 +348,20 @@ static int pcxhr_download_dsp(struct pcxhr_mgr *mgr, const struct firmware *dsp)
                data = dsp->data + i;
                if (i == 0) {
                        /* test data header consistency */
-                       len = (unsigned int)((data[0]<<16) + (data[1]<<8) + data[2]);
-                       if (len && dsp->size != (len + 2) * 3)
+                       len = (unsigned int)((data[0]<<16) +
+                                            (data[1]<<8) +
+                                            data[2]);
+                       if (len && (dsp->size != (len + 2) * 3))
                                return -EINVAL;
                }
                /* wait DSP ready for new transfer */
-               err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_TRDY,
-                                         PCXHR_ISR_HI08_TRDY, PCXHR_TIMEOUT_DSP, &dummy);
+               err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
+                                         PCXHR_ISR_HI08_TRDY,
+                                         PCXHR_ISR_HI08_TRDY,
+                                         PCXHR_TIMEOUT_DSP, &dummy);
                if (err) {
-                       snd_printk(KERN_ERR "dsp loading error at position %d\n", i);
+                       snd_printk(KERN_ERR
+                                  "dsp loading error at position %d\n", i);
                        return err;
                }
                /* send host data */
@@ -357,7 +380,8 @@ static int pcxhr_download_dsp(struct pcxhr_mgr *mgr, const struct firmware *dsp)
 /*
  * load the eeprom image
  */
-int pcxhr_load_eeprom_binary(struct pcxhr_mgr *mgr, const struct firmware *eeprom)
+int pcxhr_load_eeprom_binary(struct pcxhr_mgr *mgr,
+                            const struct firmware *eeprom)
 {
        int err;
        unsigned char reg;
@@ -365,7 +389,9 @@ int pcxhr_load_eeprom_binary(struct pcxhr_mgr *mgr, const struct firmware *eepro
        /* init value of the ICR register */
        reg = PCXHR_ICR_HI08_RREQ | PCXHR_ICR_HI08_TREQ | PCXHR_ICR_HI08_HDRQ;
        if (PCXHR_INPL(mgr, PCXHR_PLX_MBOX0) & PCXHR_MBOX0_BOOT_HERE) {
-               /* no need to load the eeprom binary, but init the HI08 interface */
+               /* no need to load the eeprom binary,
+                * but init the HI08 interface
+                */
                PCXHR_OUTPB(mgr, PCXHR_DSP_ICR, reg | PCXHR_ICR_HI08_INIT);
                msleep(PCXHR_WAIT_DEFAULT);
                PCXHR_OUTPB(mgr, PCXHR_DSP_ICR, reg);
@@ -429,8 +455,10 @@ int pcxhr_load_dsp_binary(struct pcxhr_mgr *mgr, const struct firmware *dsp)
        if (err)
                return err;
        /* wait for chk bit */
-       return pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_CHK,
-                                  PCXHR_ISR_HI08_CHK, PCXHR_TIMEOUT_DSP, &dummy);
+       return pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
+                                  PCXHR_ISR_HI08_CHK,
+                                  PCXHR_ISR_HI08_CHK,
+                                  PCXHR_TIMEOUT_DSP, &dummy);
 }
 
 
@@ -443,8 +471,8 @@ struct pcxhr_cmd_info {
 /* RMH status type */
 enum {
        RMH_SSIZE_FIXED = 0,    /* status size fix (st_length = 0..x) */
-       RMH_SSIZE_ARG = 1,      /* status size given in the LSB byte (used with st_length = 1) */
-       RMH_SSIZE_MASK = 2,     /* status size given in bitmask  (used with st_length = 1) */
+       RMH_SSIZE_ARG = 1,      /* status size given in the LSB byte */
+       RMH_SSIZE_MASK = 2,     /* status size given in bitmask */
 };
 
 /*
@@ -474,7 +502,7 @@ static struct pcxhr_cmd_info pcxhr_dsp_cmds[] = {
 [CMD_UPDATE_R_BUFFERS] =               { 0x840000, 0, RMH_SSIZE_FIXED },
 [CMD_FORMAT_STREAM_OUT] =              { 0x860000, 0, RMH_SSIZE_FIXED },
 [CMD_FORMAT_STREAM_IN] =               { 0x870000, 0, RMH_SSIZE_FIXED },
-[CMD_STREAM_SAMPLE_COUNT] =            { 0x902000, 2, RMH_SSIZE_FIXED },       /* stat_len = nb_streams * 2 */
+[CMD_STREAM_SAMPLE_COUNT] =            { 0x902000, 2, RMH_SSIZE_FIXED },
 [CMD_AUDIO_LEVEL_ADJUST] =             { 0xc22000, 0, RMH_SSIZE_FIXED },
 };
 
@@ -524,10 +552,13 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
 
        for (i = 0; i < rmh->stat_len; i++) {
                /* wait for receiver full */
-               err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_RXDF,
-                                         PCXHR_ISR_HI08_RXDF, PCXHR_TIMEOUT_DSP, &reg);
+               err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
+                                         PCXHR_ISR_HI08_RXDF,
+                                         PCXHR_ISR_HI08_RXDF,
+                                         PCXHR_TIMEOUT_DSP, &reg);
                if (err) {
-                       snd_printk(KERN_ERR "ERROR RMH stat: ISR:RXDF=1 (ISR = %x; i=%d )\n",
+                       snd_printk(KERN_ERR "ERROR RMH stat: "
+                                  "ISR:RXDF=1 (ISR = %x; i=%d )\n",
                                   reg, i);
                        return err;
                }
@@ -537,10 +568,10 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
                data |= PCXHR_INPB(mgr, PCXHR_DSP_TXL);
 
                /* need to update rmh->stat_len on the fly ?? */
-               if (i==0) {
+               if (!i) {
                        if (rmh->dsp_stat != RMH_SSIZE_FIXED) {
                                if (rmh->dsp_stat == RMH_SSIZE_ARG) {
-                                       rmh->stat_len = (u16)(data & 0x0000ff) + 1;
+                                       rmh->stat_len = (data & 0x0000ff) + 1;
                                        data &= 0xffff00;
                                } else {
                                        /* rmh->dsp_stat == RMH_SSIZE_MASK */
@@ -562,7 +593,8 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
                        rmh->stat[i] = data;
        }
        if (rmh->stat_len > max_stat_len) {
-               snd_printdd("PCXHR : rmh->stat_len=%x too big\n", rmh->stat_len);
+               snd_printdd("PCXHR : rmh->stat_len=%x too big\n",
+                           rmh->stat_len);
                rmh->stat_len = max_stat_len;
        }
        return 0;
@@ -605,7 +637,8 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
                data &= 0xff7fff;       /* MASK_1_WORD_COMMAND */
 #ifdef CONFIG_SND_DEBUG_VERBOSE
        if (rmh->cmd_idx < CMD_LAST_INDEX)
-               snd_printdd("MSG cmd[0]=%x (%s)\n", data, cmd_names[rmh->cmd_idx]);
+               snd_printdd("MSG cmd[0]=%x (%s)\n",
+                           data, cmd_names[rmh->cmd_idx]);
 #endif
 
        err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_TRDY,
@@ -619,8 +652,10 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
        if (rmh->cmd_len > 1) {
                /* send length */
                data = rmh->cmd_len - 1;
-               err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_TRDY,
-                                         PCXHR_ISR_HI08_TRDY, PCXHR_TIMEOUT_DSP, &reg);
+               err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
+                                         PCXHR_ISR_HI08_TRDY,
+                                         PCXHR_ISR_HI08_TRDY,
+                                         PCXHR_TIMEOUT_DSP, &reg);
                if (err)
                        return err;
                PCXHR_OUTPB(mgr, PCXHR_DSP_TXH, (data>>16)&0xFF);
@@ -653,8 +688,10 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
        /* test status ISR */
        if (reg & PCXHR_ISR_HI08_ERR) {
                /* ERROR, wait for receiver full */
-               err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_RXDF,
-                                         PCXHR_ISR_HI08_RXDF, PCXHR_TIMEOUT_DSP, &reg);
+               err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
+                                         PCXHR_ISR_HI08_RXDF,
+                                         PCXHR_ISR_HI08_RXDF,
+                                         PCXHR_TIMEOUT_DSP, &reg);
                if (err) {
                        snd_printk(KERN_ERR "ERROR RMH: ISR:RXDF=1 (ISR = %x)\n", reg);
                        return err;
@@ -663,7 +700,8 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
                data  = PCXHR_INPB(mgr, PCXHR_DSP_TXH) << 16;
                data |= PCXHR_INPB(mgr, PCXHR_DSP_TXM) << 8;
                data |= PCXHR_INPB(mgr, PCXHR_DSP_TXL);
-               snd_printk(KERN_ERR "ERROR RMH(%d): 0x%x\n", rmh->cmd_idx, data);
+               snd_printk(KERN_ERR "ERROR RMH(%d): 0x%x\n",
+                          rmh->cmd_idx, data);
                err = -EINVAL;
        } else {
                /* read the response data */
@@ -732,8 +770,9 @@ int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
 static inline int pcxhr_pipes_running(struct pcxhr_mgr *mgr)
 {
        int start_mask = PCXHR_INPL(mgr, PCXHR_PLX_MBOX2);
-       /* least segnificant 12 bits are the pipe states for the playback audios */
-       /* next 12 bits are the pipe states for the capture audios
+       /* least segnificant 12 bits are the pipe states
+        * for the playback audios
+        * next 12 bits are the pipe states for the capture audios
         * (PCXHR_PIPE_STATE_CAPTURE_OFFSET)
         */
        start_mask &= 0xffffff;
@@ -744,7 +783,8 @@ static inline int pcxhr_pipes_running(struct pcxhr_mgr *mgr)
 #define PCXHR_PIPE_STATE_CAPTURE_OFFSET                12
 #define MAX_WAIT_FOR_DSP                       20
 
-static int pcxhr_prepair_pipe_start(struct pcxhr_mgr *mgr, int audio_mask, int *retry)
+static int pcxhr_prepair_pipe_start(struct pcxhr_mgr *mgr,
+                                   int audio_mask, int *retry)
 {
        struct pcxhr_rmh rmh;
        int err;
@@ -760,17 +800,20 @@ static int pcxhr_prepair_pipe_start(struct pcxhr_mgr *mgr, int audio_mask, int *
                        } else {
                                /* can start capture pipe */
                                pcxhr_set_pipe_cmd_params(&rmh, 1, audio -
-                                                         PCXHR_PIPE_STATE_CAPTURE_OFFSET,
-                                                         0, 0);
+                                               PCXHR_PIPE_STATE_CAPTURE_OFFSET,
+                                               0, 0);
                        }
                        err = pcxhr_send_msg(mgr, &rmh);
                        if (err) {
                                snd_printk(KERN_ERR
-                                          "error pipe start (CMD_CAN_START_PIPE) err=%x!\n",
+                                          "error pipe start "
+                                          "(CMD_CAN_START_PIPE) err=%x!\n",
                                           err);
                                return err;
                        }
-                       /* if the pipe couldn't be prepaired for start, retry it later */
+                       /* if the pipe couldn't be prepaired for start,
+                        * retry it later
+                        */
                        if (rmh.stat[0] == 0)
                                *retry |= (1<<audio);
                }
@@ -795,14 +838,14 @@ static int pcxhr_stop_pipes(struct pcxhr_mgr *mgr, int audio_mask)
                        } else {
                                /* stop capture pipe */
                                pcxhr_set_pipe_cmd_params(&rmh, 1, audio -
-                                                         PCXHR_PIPE_STATE_CAPTURE_OFFSET,
-                                                         0, 0);
+                                               PCXHR_PIPE_STATE_CAPTURE_OFFSET,
+                                               0, 0);
                        }
                        err = pcxhr_send_msg(mgr, &rmh);
                        if (err) {
                                snd_printk(KERN_ERR
-                                          "error pipe stop (CMD_STOP_PIPE) err=%x!\n",
-                                          err);
+                                          "error pipe stop "
+                                          "(CMD_STOP_PIPE) err=%x!\n", err);
                                return err;
                        }
                }
@@ -822,15 +865,16 @@ static int pcxhr_toggle_pipes(struct pcxhr_mgr *mgr, int audio_mask)
                if (audio_mask & 1) {
                        pcxhr_init_rmh(&rmh, CMD_CONF_PIPE);
                        if (audio < PCXHR_PIPE_STATE_CAPTURE_OFFSET)
-                               pcxhr_set_pipe_cmd_params(&rmh, 0, 0, 0, 1 << audio);
+                               pcxhr_set_pipe_cmd_params(&rmh, 0, 0, 0,
+                                                         1 << audio);
                        else
                                pcxhr_set_pipe_cmd_params(&rmh, 1, 0, 0,
                                                          1 << (audio - PCXHR_PIPE_STATE_CAPTURE_OFFSET));
                        err = pcxhr_send_msg(mgr, &rmh);
                        if (err) {
                                snd_printk(KERN_ERR
-                                          "error pipe start (CMD_CONF_PIPE) err=%x!\n",
-                                          err);
+                                          "error pipe start "
+                                          "(CMD_CONF_PIPE) err=%x!\n", err);
                                return err;
                        }
                }
@@ -841,7 +885,9 @@ static int pcxhr_toggle_pipes(struct pcxhr_mgr *mgr, int audio_mask)
        pcxhr_init_rmh(&rmh, CMD_SEND_IRQA);
        err = pcxhr_send_msg(mgr, &rmh);
        if (err) {
-               snd_printk(KERN_ERR "error pipe start (CMD_SEND_IRQA) err=%x!\n", err );
+               snd_printk(KERN_ERR
+                          "error pipe start (CMD_SEND_IRQA) err=%x!\n",
+                          err);
                return err;
        }
        return 0;
@@ -849,7 +895,8 @@ static int pcxhr_toggle_pipes(struct pcxhr_mgr *mgr, int audio_mask)
 
 
 
-int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_mask, int start)
+int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask,
+                        int capture_mask, int start)
 {
        int state, i, err;
        int audio_mask;
@@ -858,21 +905,23 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m
        struct timeval my_tv1, my_tv2;
        do_gettimeofday(&my_tv1);
 #endif
-       audio_mask = (playback_mask | (capture_mask << PCXHR_PIPE_STATE_CAPTURE_OFFSET));
+       audio_mask = (playback_mask |
+                     (capture_mask << PCXHR_PIPE_STATE_CAPTURE_OFFSET));
        /* current pipe state (playback + record) */
        state = pcxhr_pipes_running(mgr);
        snd_printdd("pcxhr_set_pipe_state %s (mask %x current %x)\n",
                    start ? "START" : "STOP", audio_mask, state);
        if (start) {
-               audio_mask &= ~state;   /* start only pipes that are not yet started */
+               /* start only pipes that are not yet started */
+               audio_mask &= ~state;
                state = audio_mask;
                for (i = 0; i < MAX_WAIT_FOR_DSP; i++) {
                        err = pcxhr_prepair_pipe_start(mgr, state, &state);
                        if (err)
                                return err;
                        if (state == 0)
-                               break;  /* success, all pipes prepaired for start */
-                       mdelay(1);              /* otherwise wait 1 millisecond and retry */
+                               break;  /* success, all pipes prepaired */
+                       mdelay(1);      /* wait 1 millisecond and retry */
                }
        } else {
                audio_mask &= state;    /* stop only pipes that are started */
@@ -891,7 +940,7 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m
                if ((state & audio_mask) == (start ? audio_mask : 0))
                        break;
                if (++i >= MAX_WAIT_FOR_DSP * 100) {
-                       snd_printk(KERN_ERR "error pipe start/stop (ED_NO_RESPONSE_AT_IRQA)\n");
+                       snd_printk(KERN_ERR "error pipe start/stop\n");
                        return -EBUSY;
                }
                udelay(10);                     /* wait 10 microseconds */
@@ -918,7 +967,8 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,
 
        spin_lock_irqsave(&mgr->msg_lock, flags);
        if ((mgr->io_num_reg_cont & mask) == value) {
-               snd_printdd("IO_NUM_REG_CONT mask %x already is set to %x\n", mask, value);
+               snd_printdd("IO_NUM_REG_CONT mask %x already is set to %x\n",
+                           mask, value);
                if (changed)
                        *changed = 0;
                spin_unlock_irqrestore(&mgr->msg_lock, flags);
@@ -971,7 +1021,8 @@ static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err,
                err = ((err >> 12) & 0xfff);
        if (!err)
                return 0;
-       snd_printdd("CMD_ASYNC : Error %s %s Pipe %d err=%x\n", err_src_name[err_src],
+       snd_printdd("CMD_ASYNC : Error %s %s Pipe %d err=%x\n",
+                   err_src_name[err_src],
                    is_capture ? "Record" : "Play", pipe, err);
        if (err == 0xe01)
                mgr->async_err_stream_xrun++;
@@ -996,6 +1047,13 @@ void pcxhr_msg_tasklet(unsigned long arg)
                snd_printdd("TASKLET : PCXHR_IRQ_TIME_CODE event occured\n");
        if (mgr->src_it_dsp & PCXHR_IRQ_NOTIFY)
                snd_printdd("TASKLET : PCXHR_IRQ_NOTIFY event occured\n");
+       if (mgr->src_it_dsp & (PCXHR_IRQ_FREQ_CHANGE | PCXHR_IRQ_TIME_CODE)) {
+               /* clear events FREQ_CHANGE and TIME_CODE */
+               pcxhr_init_rmh(prmh, CMD_TEST_IT);
+               err = pcxhr_send_msg(mgr, prmh);
+               snd_printdd("CMD_TEST_IT : err=%x, stat=%x\n",
+                           err, prmh->stat[0]);
+       }
        if (mgr->src_it_dsp & PCXHR_IRQ_ASYNC) {
                snd_printdd("TASKLET : PCXHR_IRQ_ASYNC event occured\n");
 
@@ -1005,18 +1063,22 @@ void pcxhr_msg_tasklet(unsigned long arg)
                prmh->stat_len = PCXHR_SIZE_MAX_LONG_STATUS;
                err = pcxhr_send_msg(mgr, prmh);
                if (err)
-                       snd_printk(KERN_ERR "ERROR pcxhr_msg_tasklet=%x;\n", err);
+                       snd_printk(KERN_ERR "ERROR pcxhr_msg_tasklet=%x;\n",
+                                  err);
                i = 1;
                while (i < prmh->stat_len) {
-                       int nb_audio = (prmh->stat[i] >> FIELD_SIZE) & MASK_FIRST_FIELD;
-                       int nb_stream = (prmh->stat[i] >> (2*FIELD_SIZE)) & MASK_FIRST_FIELD;
+                       int nb_audio = ((prmh->stat[i] >> FIELD_SIZE) &
+                                       MASK_FIRST_FIELD);
+                       int nb_stream = ((prmh->stat[i] >> (2*FIELD_SIZE)) &
+                                        MASK_FIRST_FIELD);
                        int pipe = prmh->stat[i] & MASK_FIRST_FIELD;
                        int is_capture = prmh->stat[i] & 0x400000;
                        u32 err2;
 
                        if (prmh->stat[i] & 0x800000) { /* if BIT_END */
                                snd_printdd("TASKLET : End%sPipe %d\n",
-                                           is_capture ? "Record" : "Play", pipe);
+                                           is_capture ? "Record" : "Play",
+                                           pipe);
                        }
                        i++;
                        err2 = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1];
@@ -1062,7 +1124,7 @@ static u_int64_t pcxhr_stream_read_position(struct pcxhr_mgr *mgr,
        pcxhr_init_rmh(&rmh, CMD_STREAM_SAMPLE_COUNT);
        pcxhr_set_pipe_cmd_params(&rmh, stream->pipe->is_capture,
                                  stream->pipe->first_audio, 0, stream_mask);
-       /* rmh.stat_len = 2; */         /* 2 resp data for each stream of the pipe */
+       /* rmh.stat_len = 2; */ /* 2 resp data for each stream of the pipe */
 
        err = pcxhr_send_msg(mgr, &rmh);
        if (err)
@@ -1072,18 +1134,21 @@ static u_int64_t pcxhr_stream_read_position(struct pcxhr_mgr *mgr,
        hw_sample_count += (u_int64_t)rmh.stat[1];
 
        snd_printdd("stream %c%d : abs samples real(%ld) timer(%ld)\n",
-                   stream->pipe->is_capture ? 'C':'P', stream->substream->number,
+                   stream->pipe->is_capture ? 'C' : 'P',
+                   stream->substream->number,
                    (long unsigned int)hw_sample_count,
                    (long unsigned int)(stream->timer_abs_periods +
-                                       stream->timer_period_frag + PCXHR_GRANULARITY));
-
+                                       stream->timer_period_frag +
+                                       mgr->granularity));
        return hw_sample_count;
 }
 
 static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
-                                  struct pcxhr_stream *stream, int samples_to_add)
+                                  struct pcxhr_stream *stream,
+                                  int samples_to_add)
 {
-       if (stream->substream && (stream->status == PCXHR_STREAM_STATUS_RUNNING)) {
+       if (stream->substream &&
+           (stream->status == PCXHR_STREAM_STATUS_RUNNING)) {
                u_int64_t new_sample_count;
                int elapsed = 0;
                int hardware_read = 0;
@@ -1092,20 +1157,22 @@ static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
                if (samples_to_add < 0) {
                        stream->timer_is_synced = 0;
                        /* add default if no hardware_read possible */
-                       samples_to_add = PCXHR_GRANULARITY;
+                       samples_to_add = mgr->granularity;
                }
 
                if (!stream->timer_is_synced) {
-                       if (stream->timer_abs_periods != 0 ||
-                           stream->timer_period_frag + PCXHR_GRANULARITY >=
-                           runtime->period_size) {
-                               new_sample_count = pcxhr_stream_read_position(mgr, stream);
+                       if ((stream->timer_abs_periods != 0) ||
+                           ((stream->timer_period_frag + samples_to_add) >=
+                           runtime->period_size)) {
+                               new_sample_count =
+                                 pcxhr_stream_read_position(mgr, stream);
                                hardware_read = 1;
-                               if (new_sample_count >= PCXHR_GRANULARITY_MIN) {
-                                       /* sub security offset because of jitter and
-                                        * finer granularity of dsp time (MBOX4)
+                               if (new_sample_count >= mgr->granularity) {
+                                       /* sub security offset because of
+                                        * jitter and finer granularity of
+                                        * dsp time (MBOX4)
                                         */
-                                       new_sample_count -= PCXHR_GRANULARITY_MIN;
+                                       new_sample_count -= mgr->granularity;
                                        stream->timer_is_synced = 1;
                                }
                        }
@@ -1128,12 +1195,15 @@ static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
                                stream->timer_buf_periods = 0;
                        stream->timer_abs_periods = new_elapse_pos;
                }
-               if (new_sample_count >= stream->timer_abs_periods)
-                       stream->timer_period_frag = (u_int32_t)(new_sample_count -
-                                                               stream->timer_abs_periods);
-               else
-                       snd_printk(KERN_ERR "ERROR new_sample_count too small ??? %lx\n",
+               if (new_sample_count >= stream->timer_abs_periods) {
+                       stream->timer_period_frag =
+                               (u_int32_t)(new_sample_count -
+                                           stream->timer_abs_periods);
+               } else {
+                       snd_printk(KERN_ERR
+                                  "ERROR new_sample_count too small ??? %ld\n",
                                   (long unsigned int)new_sample_count);
+               }
 
                if (elapsed) {
                        spin_unlock(&mgr->lock);
@@ -1143,7 +1213,6 @@ static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
        }
 }
 
-
 irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
 {
        struct pcxhr_mgr *mgr = dev_id;
@@ -1156,7 +1225,8 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
        reg = PCXHR_INPL(mgr, PCXHR_PLX_IRQCS);
        if (! (reg & PCXHR_IRQCS_ACTIVE_PCIDB)) {
                spin_unlock(&mgr->lock);
-               return IRQ_NONE;        /* this device did not cause the interrupt */
+               /* this device did not cause the interrupt */
+               return IRQ_NONE;
        }
 
        /* clear interrupt */
@@ -1167,10 +1237,12 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
        if (reg & PCXHR_IRQ_TIMER) {
                int timer_toggle = reg & PCXHR_IRQ_TIMER;
                /* is a 24 bit counter */
-               int dsp_time_new = PCXHR_INPL(mgr, PCXHR_PLX_MBOX4) & PCXHR_DSP_TIME_MASK;
+               int dsp_time_new =
+                       PCXHR_INPL(mgr, PCXHR_PLX_MBOX4) & PCXHR_DSP_TIME_MASK;
                int dsp_time_diff = dsp_time_new - mgr->dsp_time_last;
 
-               if (dsp_time_diff < 0 && mgr->dsp_time_last != PCXHR_DSP_TIME_INVALID) {
+               if ((dsp_time_diff < 0) &&
+                   (mgr->dsp_time_last != PCXHR_DSP_TIME_INVALID)) {
                        snd_printdd("ERROR DSP TIME old(%d) new(%d) -> "
                                    "resynchronize all streams\n",
                                    mgr->dsp_time_last, dsp_time_new);
@@ -1178,42 +1250,51 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
                }
 #ifdef CONFIG_SND_DEBUG_VERBOSE
                if (dsp_time_diff == 0)
-                       snd_printdd("ERROR DSP TIME NO DIFF time(%d)\n", dsp_time_new);
-               else if (dsp_time_diff >= (2*PCXHR_GRANULARITY))
+                       snd_printdd("ERROR DSP TIME NO DIFF time(%d)\n",
+                                   dsp_time_new);
+               else if (dsp_time_diff >= (2*mgr->granularity))
                        snd_printdd("ERROR DSP TIME TOO BIG old(%d) add(%d)\n",
-                                   mgr->dsp_time_last, dsp_time_new - mgr->dsp_time_last);
+                                   mgr->dsp_time_last,
+                                   dsp_time_new - mgr->dsp_time_last);
+               else if (dsp_time_diff % mgr->granularity)
+                       snd_printdd("ERROR DSP TIME increased by %d\n",
+                                   dsp_time_diff);
 #endif
                mgr->dsp_time_last = dsp_time_new;
 
-               if (timer_toggle == mgr->timer_toggle)
+               if (timer_toggle == mgr->timer_toggle) {
                        snd_printdd("ERROR TIMER TOGGLE\n");
+                       mgr->dsp_time_err++;
+               }
                mgr->timer_toggle = timer_toggle;
 
                reg &= ~PCXHR_IRQ_TIMER;
                for (i = 0; i < mgr->num_cards; i++) {
                        chip = mgr->chip[i];
                        for (j = 0; j < chip->nb_streams_capt; j++)
-                               pcxhr_update_timer_pos(mgr, &chip->capture_stream[j],
-                                                      dsp_time_diff);
+                               pcxhr_update_timer_pos(mgr,
+                                               &chip->capture_stream[j],
+                                               dsp_time_diff);
                }
                for (i = 0; i < mgr->num_cards; i++) {
                        chip = mgr->chip[i];
                        for (j = 0; j < chip->nb_streams_play; j++)
-                               pcxhr_update_timer_pos(mgr, &chip->playback_stream[j],
-                                                      dsp_time_diff);
+                               pcxhr_update_timer_pos(mgr,
+                                               &chip->playback_stream[j],
+                                               dsp_time_diff);
                }
        }
        /* other irq's handled in the tasklet */
        if (reg & PCXHR_IRQ_MASK) {
-
-               /* as we didn't request any notifications, some kind of xrun error
-                * will probably occured
-                */
-               /* better resynchronize all streams next interrupt : */
-               mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
-               
+               if (reg & PCXHR_IRQ_ASYNC) {
+                       /* as we didn't request any async notifications,
+                        * some kind of xrun error will probably occured
+                        */
+                       /* better resynchronize all streams next interrupt : */
+                       mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
+               }
                mgr->src_it_dsp = reg;
-               tasklet_hi_schedule(&mgr->msg_taskq);
+               tasklet_schedule(&mgr->msg_taskq);
        }
 #ifdef CONFIG_SND_DEBUG_VERBOSE
        if (reg & PCXHR_FATAL_DSP_ERR)
index d9a4ab6..bbbd66d 100644 (file)
@@ -65,7 +65,7 @@ enum {
        CMD_RESYNC_AUDIO_INPUTS,        /* cmd_len = 1  stat_len = 0 */
        CMD_GET_DSP_RESOURCES,          /* cmd_len = 1  stat_len = 4 */
        CMD_SET_TIMER_INTERRUPT,        /* cmd_len = 1  stat_len = 0 */
-       CMD_RES_PIPE,                   /* cmd_len 2  stat_len = 0 */
+       CMD_RES_PIPE,                   /* cmd_len >=2  stat_len = 0 */
        CMD_FREE_PIPE,                  /* cmd_len = 1  stat_len = 0 */
        CMD_CONF_PIPE,                  /* cmd_len = 2  stat_len = 0 */
        CMD_STOP_PIPE,                  /* cmd_len = 1  stat_len = 0 */
@@ -96,6 +96,8 @@ void pcxhr_init_rmh(struct pcxhr_rmh *rmh, int cmd);
 void pcxhr_set_pipe_cmd_params(struct pcxhr_rmh* rmh, int capture, unsigned int param1,
                               unsigned int param2, unsigned int param3);
 
+#define DSP_EXT_CMD_SET(x) (x->dsp_version > 0x012800)
+
 /*
  send the rmh
  */
@@ -110,6 +112,7 @@ int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh);
 #define IO_NUM_REG_STATUS              5
 #define IO_NUM_REG_CUER                        10
 #define IO_NUM_UER_CHIP_REG            11
+#define IO_NUM_REG_CONFIG_SRC          12
 #define IO_NUM_REG_OUT_ANA_LEVEL       20
 #define IO_NUM_REG_IN_ANA_LEVEL                21
 
index 96640d9..592743a 100644 (file)
@@ -31,6 +31,7 @@
 #include "pcxhr_mixer.h"
 #include "pcxhr_hwdep.h"
 #include "pcxhr_core.h"
+#include "pcxhr_mix22.h"
 
 
 #if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
 #endif
 
 
+static int pcxhr_sub_init(struct pcxhr_mgr *mgr);
 /*
  * get basic information and init pcxhr card
  */
-
 static int pcxhr_init_board(struct pcxhr_mgr *mgr)
 {
        int err;
@@ -68,7 +69,7 @@ static int pcxhr_init_board(struct pcxhr_mgr *mgr)
        if ((rmh.stat[0] & MASK_FIRST_FIELD) != mgr->playback_chips * 2)
                return -EINVAL;
        /* test 8 or 2 phys in */
-       if (((rmh.stat[0] >> (2 * FIELD_SIZE)) & MASK_FIRST_FIELD) !=
+       if (((rmh.stat[0] >> (2 * FIELD_SIZE)) & MASK_FIRST_FIELD) <
            mgr->capture_chips * 2)
                return -EINVAL;
        /* test max nb substream per board */
@@ -77,20 +78,34 @@ static int pcxhr_init_board(struct pcxhr_mgr *mgr)
        /* test max nb substream per pipe */
        if (((rmh.stat[1] >> 7) & 0x5F) < PCXHR_PLAYBACK_STREAMS)
                return -EINVAL;
+       snd_printdd("supported formats : playback=%x capture=%x\n",
+                   rmh.stat[2], rmh.stat[3]);
 
        pcxhr_init_rmh(&rmh, CMD_VERSION);
        /* firmware num for DSP */
        rmh.cmd[0] |= mgr->firmware_num;
        /* transfer granularity in samples (should be multiple of 48) */
-       rmh.cmd[1] = (1<<23) + PCXHR_GRANULARITY;
+       rmh.cmd[1] = (1<<23) + mgr->granularity;
        rmh.cmd_len = 2;
        err = pcxhr_send_msg(mgr, &rmh);
        if (err)
                return err;
-       snd_printdd("PCXHR DSP version is %d.%d.%d\n",
-                   (rmh.stat[0]>>16)&0xff, (rmh.stat[0]>>8)&0xff, rmh.stat[0]&0xff);
+       snd_printdd("PCXHR DSP version is %d.%d.%d\n", (rmh.stat[0]>>16)&0xff,
+                   (rmh.stat[0]>>8)&0xff, rmh.stat[0]&0xff);
        mgr->dsp_version = rmh.stat[0];
 
+       if (mgr->is_hr_stereo)
+               err = hr222_sub_init(mgr);
+       else
+               err = pcxhr_sub_init(mgr);
+       return err;
+}
+
+static int pcxhr_sub_init(struct pcxhr_mgr *mgr)
+{
+       int err;
+       struct pcxhr_rmh rmh;
+
        /* get options */
        pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);
        rmh.cmd[0] |= IO_NUM_REG_STATUS;
@@ -100,20 +115,22 @@ static int pcxhr_init_board(struct pcxhr_mgr *mgr)
        if (err)
                return err;
 
-       if ((rmh.stat[1] & REG_STATUS_OPT_DAUGHTER_MASK) == REG_STATUS_OPT_ANALOG_BOARD)
-               mgr->board_has_analog = 1;      /* analog addon board available */
-       else
-               /* analog addon board not available -> no support for instance */
-               return -EINVAL; 
+       if ((rmh.stat[1] & REG_STATUS_OPT_DAUGHTER_MASK) ==
+           REG_STATUS_OPT_ANALOG_BOARD)
+               mgr->board_has_analog = 1;      /* analog addon board found */
 
        /* unmute inputs */
        err = pcxhr_write_io_num_reg_cont(mgr, REG_CONT_UNMUTE_INPUTS,
                                          REG_CONT_UNMUTE_INPUTS, NULL);
        if (err)
                return err;
-       /* unmute outputs */
-       pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ); /* a write to IO_NUM_REG_MUTE_OUT mutes! */
+       /* unmute outputs (a write to IO_NUM_REG_MUTE_OUT mutes!) */
+       pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);
        rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
+       if (DSP_EXT_CMD_SET(mgr)) {
+               rmh.cmd[1]  = 1;        /* unmute digital plugs */
+               rmh.cmd_len = 2;
+       }
        err = pcxhr_send_msg(mgr, &rmh);
        return err;
 }
@@ -124,19 +141,25 @@ void pcxhr_reset_board(struct pcxhr_mgr *mgr)
 
        if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
                /* mute outputs */
+           if (!mgr->is_hr_stereo) {
                /* a read to IO_NUM_REG_MUTE_OUT register unmutes! */
                pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
                rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
                pcxhr_send_msg(mgr, &rmh);
                /* mute inputs */
-               pcxhr_write_io_num_reg_cont(mgr, REG_CONT_UNMUTE_INPUTS, 0, NULL);
+               pcxhr_write_io_num_reg_cont(mgr, REG_CONT_UNMUTE_INPUTS,
+                                           0, NULL);
+           }
+               /* stereo cards mute with reset of dsp */
        }
        /* reset pcxhr dsp */
-       if (mgr->dsp_loaded & ( 1 << PCXHR_FIRMWARE_DSP_EPRM_INDEX))
+       if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_EPRM_INDEX))
                pcxhr_reset_dsp(mgr);
        /* reset second xilinx */
-       if (mgr->dsp_loaded & ( 1 << PCXHR_FIRMWARE_XLX_COM_INDEX))
+       if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_XLX_COM_INDEX)) {
                pcxhr_reset_xilinx_com(mgr);
+               mgr->dsp_loaded = 1;
+       }
        return;
 }
 
@@ -144,8 +167,9 @@ void pcxhr_reset_board(struct pcxhr_mgr *mgr)
 /*
  *  allocate a playback/capture pipe (pcmp0/pcmc0)
  */
-static int pcxhr_dsp_allocate_pipe( struct pcxhr_mgr *mgr, struct pcxhr_pipe *pipe,
-                                   int is_capture, int pin)
+static int pcxhr_dsp_allocate_pipe(struct pcxhr_mgr *mgr,
+                                  struct pcxhr_pipe *pipe,
+                                  int is_capture, int pin)
 {
        int stream_count, audio_count;
        int err;
@@ -161,15 +185,23 @@ static int pcxhr_dsp_allocate_pipe( struct pcxhr_mgr *mgr, struct pcxhr_pipe *pi
                stream_count = PCXHR_PLAYBACK_STREAMS;
                audio_count = 2;        /* always stereo */
        }
-       snd_printdd("snd_add_ref_pipe pin(%d) pcm%c0\n", pin, is_capture ? 'c' : 'p');
+       snd_printdd("snd_add_ref_pipe pin(%d) pcm%c0\n",
+                   pin, is_capture ? 'c' : 'p');
        pipe->is_capture = is_capture;
        pipe->first_audio = pin;
        /* define pipe (P_PCM_ONLY_MASK (0x020000) is not necessary) */
        pcxhr_init_rmh(&rmh, CMD_RES_PIPE);
-       pcxhr_set_pipe_cmd_params(&rmh, is_capture, pin, audio_count, stream_count); 
+       pcxhr_set_pipe_cmd_params(&rmh, is_capture, pin,
+                                 audio_count, stream_count);
+       rmh.cmd[1] |= 0x020000; /* add P_PCM_ONLY_MASK */
+       if (DSP_EXT_CMD_SET(mgr)) {
+               /* add channel mask to command */
+         rmh.cmd[rmh.cmd_len++] = (audio_count == 1) ? 0x01 : 0x03;
+       }
        err = pcxhr_send_msg(mgr, &rmh);
        if (err < 0) {
-               snd_printk(KERN_ERR "error pipe allocation (CMD_RES_PIPE) err=%x!\n", err );
+               snd_printk(KERN_ERR "error pipe allocation "
+                          "(CMD_RES_PIPE) err=%x!\n", err);
                return err;
        }
        pipe->status = PCXHR_PIPE_DEFINED;
@@ -199,10 +231,12 @@ static int pcxhr_dsp_free_pipe( struct pcxhr_mgr *mgr, struct pcxhr_pipe *pipe)
                snd_printk(KERN_ERR "error stopping pipe!\n");
        /* release the pipe */
        pcxhr_init_rmh(&rmh, CMD_FREE_PIPE);
-       pcxhr_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->first_audio, 0, 0);
+       pcxhr_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->first_audio,
+                                 0, 0);
        err = pcxhr_send_msg(mgr, &rmh);
        if (err < 0)
-               snd_printk(KERN_ERR "error pipe release (CMD_FREE_PIPE) err(%x)\n", err);
+               snd_printk(KERN_ERR "error pipe release "
+                          "(CMD_FREE_PIPE) err(%x)\n", err);
        pipe->status = PCXHR_PIPE_UNDEFINED;
        return err;
 }
@@ -248,15 +282,16 @@ static int pcxhr_start_pipes(struct pcxhr_mgr *mgr)
        for (i = 0; i < mgr->num_cards; i++) {
                chip = mgr->chip[i];
                if (chip->nb_streams_play)
-                       playback_mask |= (1 << chip->playback_pipe.first_audio);
+                       playback_mask |= 1 << chip->playback_pipe.first_audio;
                for (j = 0; j < chip->nb_streams_capt; j++)
-                       capture_mask |= (1 << chip->capture_pipe[j].first_audio);
+                       capture_mask |= 1 << chip->capture_pipe[j].first_audio;
        }
        return pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1);
 }
 
 
-static int pcxhr_dsp_load(struct pcxhr_mgr *mgr, int index, const struct firmware *dsp)
+static int pcxhr_dsp_load(struct pcxhr_mgr *mgr, int index,
+                         const struct firmware *dsp)
 {
        int err, card_index;
 
@@ -330,22 +365,33 @@ static int pcxhr_dsp_load(struct pcxhr_mgr *mgr, int index, const struct firmwar
 
 int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
 {
-       static char *fw_files[5] = {
-               "xi_1_882.dat",
-               "xc_1_882.dat",
-               "e321_512.e56",
-               "b321_512.b56",
-               "d321_512.d56"
+       static char *fw_files[][5] = {
+       [0] = { "xlxint.dat", "xlxc882hr.dat",
+               "dspe882.e56", "dspb882hr.b56", "dspd882.d56" },
+       [1] = { "xlxint.dat", "xlxc882e.dat",
+               "dspe882.e56", "dspb882e.b56", "dspd882.d56" },
+       [2] = { "xlxint.dat", "xlxc1222hr.dat",
+               "dspe882.e56", "dspb1222hr.b56", "dspd1222.d56" },
+       [3] = { "xlxint.dat", "xlxc1222e.dat",
+               "dspe882.e56", "dspb1222e.b56", "dspd1222.d56" },
+       [4] = { NULL, "xlxc222.dat",
+               "dspe924.e56", "dspb924.b56", "dspd222.d56" },
+       [5] = { NULL, "xlxc924.dat",
+               "dspe924.e56", "dspb924.b56", "dspd222.d56" },
        };
        char path[32];
 
        const struct firmware *fw_entry;
        int i, err;
+       int fw_set = mgr->fw_file_set;
 
-       for (i = 0; i < ARRAY_SIZE(fw_files); i++) {
-               sprintf(path, "pcxhr/%s", fw_files[i]);
+       for (i = 0; i < 5; i++) {
+               if (!fw_files[fw_set][i])
+                       continue;
+               sprintf(path, "pcxhr/%s", fw_files[fw_set][i]);
                if (request_firmware(&fw_entry, path, &mgr->pci->dev)) {
-                       snd_printk(KERN_ERR "pcxhr: can't load firmware %s\n", path);
+                       snd_printk(KERN_ERR "pcxhr: can't load firmware %s\n",
+                                  path);
                        return -ENOENT;
                }
                /* fake hwdep dsp record */
@@ -358,11 +404,26 @@ int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
        return 0;
 }
 
-MODULE_FIRMWARE("pcxhr/xi_1_882.dat");
-MODULE_FIRMWARE("pcxhr/xc_1_882.dat");
-MODULE_FIRMWARE("pcxhr/e321_512.e56");
-MODULE_FIRMWARE("pcxhr/b321_512.b56");
-MODULE_FIRMWARE("pcxhr/d321_512.d56");
+MODULE_FIRMWARE("pcxhr/xlxint.dat");
+MODULE_FIRMWARE("pcxhr/xlxc882hr.dat");
+MODULE_FIRMWARE("pcxhr/xlxc882e.dat");
+MODULE_FIRMWARE("pcxhr/dspe882.e56");
+MODULE_FIRMWARE("pcxhr/dspb882hr.b56");
+MODULE_FIRMWARE("pcxhr/dspb882e.b56");
+MODULE_FIRMWARE("pcxhr/dspd882.d56");
+
+MODULE_FIRMWARE("pcxhr/xlxc1222hr.dat");
+MODULE_FIRMWARE("pcxhr/xlxc1222e.dat");
+MODULE_FIRMWARE("pcxhr/dspb1222hr.b56");
+MODULE_FIRMWARE("pcxhr/dspb1222e.b56");
+MODULE_FIRMWARE("pcxhr/dspd1222.d56");
+
+MODULE_FIRMWARE("pcxhr/xlxc222.dat");
+MODULE_FIRMWARE("pcxhr/xlxc924.dat");
+MODULE_FIRMWARE("pcxhr/dspe924.e56");
+MODULE_FIRMWARE("pcxhr/dspb924.b56");
+MODULE_FIRMWARE("pcxhr/dspd222.d56");
+
 
 #else /* old style firmware loading */
 
@@ -373,7 +434,8 @@ MODULE_FIRMWARE("pcxhr/d321_512.d56");
 static int pcxhr_hwdep_dsp_status(struct snd_hwdep *hw,
                                  struct snd_hwdep_dsp_status *info)
 {
-       strcpy(info->id, "pcxhr");
+       struct pcxhr_mgr *mgr = hw->private_data;
+       sprintf(info->id, "pcxhr%d", mgr->fw_file_set);
         info->num_dsps = PCXHR_FIRMWARE_FILES_MAX_INDEX;
 
        if (hw->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX))
@@ -393,8 +455,8 @@ static int pcxhr_hwdep_dsp_load(struct snd_hwdep *hw,
        fw.size = dsp->length;
        fw.data = vmalloc(fw.size);
        if (! fw.data) {
-               snd_printk(KERN_ERR "pcxhr: cannot allocate dsp image (%lu bytes)\n",
-                          (unsigned long)fw.size);
+               snd_printk(KERN_ERR "pcxhr: cannot allocate dsp image "
+                          "(%lu bytes)\n", (unsigned long)fw.size);
                return -ENOMEM;
        }
        if (copy_from_user((void *)fw.data, dsp->image, dsp->length)) {
@@ -424,8 +486,11 @@ int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
        int err;
        struct snd_hwdep *hw;
 
-       /* only create hwdep interface for first cardX (see "index" module parameter)*/
-       if ((err = snd_hwdep_new(mgr->chip[0]->card, PCXHR_HWDEP_ID, 0, &hw)) < 0)
+       /* only create hwdep interface for first cardX
+        * (see "index" module parameter)
+        */
+       err = snd_hwdep_new(mgr->chip[0]->card, PCXHR_HWDEP_ID, 0, &hw);
+       if (err < 0)
                return err;
 
        hw->iface = SNDRV_HWDEP_IFACE_PCXHR;
@@ -435,10 +500,13 @@ int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
        hw->ops.dsp_status = pcxhr_hwdep_dsp_status;
        hw->ops.dsp_load = pcxhr_hwdep_dsp_load;
        hw->exclusive = 1;
+       /* stereo cards don't need fw_file_0 -> dsp_loaded = 1 */
+       hw->dsp_loaded = mgr->is_hr_stereo ? 1 : 0;
        mgr->dsp_loaded = 0;
        sprintf(hw->name, PCXHR_HWDEP_ID);
 
-       if ((err = snd_card_register(mgr->chip[0]->card)) < 0)
+       err = snd_card_register(mgr->chip[0]->card);
+       if (err < 0)
                return err;
        return 0;
 }
diff --git a/sound/pci/pcxhr/pcxhr_mix22.c b/sound/pci/pcxhr/pcxhr_mix22.c
new file mode 100644 (file)
index 0000000..ff01912
--- /dev/null
@@ -0,0 +1,820 @@
+/*
+ * Driver for Digigram pcxhr compatible soundcards
+ *
+ * mixer interface for stereo cards
+ *
+ * Copyright (c) 2004 by Digigram <alsa@digigram.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/asoundef.h>
+#include "pcxhr.h"
+#include "pcxhr_core.h"
+#include "pcxhr_mix22.h"
+
+
+/* registers used on the DSP and Xilinx (port 2) : HR stereo cards only */
+#define PCXHR_DSP_RESET                0x20
+#define PCXHR_XLX_CFG          0x24
+#define PCXHR_XLX_RUER         0x28
+#define PCXHR_XLX_DATA         0x2C
+#define PCXHR_XLX_STATUS       0x30
+#define PCXHR_XLX_LOFREQ       0x34
+#define PCXHR_XLX_HIFREQ       0x38
+#define PCXHR_XLX_CSUER                0x3C
+#define PCXHR_XLX_SELMIC       0x40
+
+#define PCXHR_DSP 2
+
+/* byte access only ! */
+#define PCXHR_INPB(mgr, x)     inb((mgr)->port[PCXHR_DSP] + (x))
+#define PCXHR_OUTPB(mgr, x, data) outb((data), (mgr)->port[PCXHR_DSP] + (x))
+
+
+/* values for PCHR_DSP_RESET register */
+#define PCXHR_DSP_RESET_DSP    0x01
+#define PCXHR_DSP_RESET_MUTE   0x02
+#define PCXHR_DSP_RESET_CODEC  0x08
+
+/* values for PCHR_XLX_CFG register */
+#define PCXHR_CFG_SYNCDSP_MASK         0x80
+#define PCXHR_CFG_DEPENDENCY_MASK      0x60
+#define PCXHR_CFG_INDEPENDANT_SEL      0x00
+#define PCXHR_CFG_MASTER_SEL           0x40
+#define PCXHR_CFG_SLAVE_SEL            0x20
+#define PCXHR_CFG_DATA_UER1_SEL_MASK   0x10    /* 0 (UER0), 1(UER1) */
+#define PCXHR_CFG_DATAIN_SEL_MASK      0x08    /* 0 (ana), 1 (UER) */
+#define PCXHR_CFG_SRC_MASK             0x04    /* 0 (Bypass), 1 (SRC Actif) */
+#define PCXHR_CFG_CLOCK_UER1_SEL_MASK  0x02    /* 0 (UER0), 1(UER1) */
+#define PCXHR_CFG_CLOCKIN_SEL_MASK     0x01    /* 0 (internal), 1 (AES/EBU) */
+
+/* values for PCHR_XLX_DATA register */
+#define PCXHR_DATA_CODEC       0x80
+#define AKM_POWER_CONTROL_CMD  0xA007
+#define AKM_RESET_ON_CMD       0xA100
+#define AKM_RESET_OFF_CMD      0xA103
+#define AKM_CLOCK_INF_55K_CMD  0xA240
+#define AKM_CLOCK_SUP_55K_CMD  0xA24D
+#define AKM_MUTE_CMD           0xA38D
+#define AKM_UNMUTE_CMD         0xA30D
+#define AKM_LEFT_LEVEL_CMD     0xA600
+#define AKM_RIGHT_LEVEL_CMD    0xA700
+
+/* values for PCHR_XLX_STATUS register - READ */
+#define PCXHR_STAT_SRC_LOCK            0x01
+#define PCXHR_STAT_LEVEL_IN            0x02
+#define PCXHR_STAT_MIC_CAPS            0x10
+/* values for PCHR_XLX_STATUS register - WRITE */
+#define PCXHR_STAT_FREQ_SYNC_MASK      0x01
+#define PCXHR_STAT_FREQ_UER1_MASK      0x02
+#define PCXHR_STAT_FREQ_SAVE_MASK      0x80
+
+/* values for PCHR_XLX_CSUER register */
+#define PCXHR_SUER1_BIT_U_READ_MASK    0x80
+#define PCXHR_SUER1_BIT_C_READ_MASK    0x40
+#define PCXHR_SUER1_DATA_PRESENT_MASK  0x20
+#define PCXHR_SUER1_CLOCK_PRESENT_MASK 0x10
+#define PCXHR_SUER_BIT_U_READ_MASK     0x08
+#define PCXHR_SUER_BIT_C_READ_MASK     0x04
+#define PCXHR_SUER_DATA_PRESENT_MASK   0x02
+#define PCXHR_SUER_CLOCK_PRESENT_MASK  0x01
+
+#define PCXHR_SUER_BIT_U_WRITE_MASK    0x02
+#define PCXHR_SUER_BIT_C_WRITE_MASK    0x01
+
+/* values for PCXHR_XLX_SELMIC register - WRITE */
+#define PCXHR_SELMIC_PREAMPLI_OFFSET   2
+#define PCXHR_SELMIC_PREAMPLI_MASK     0x0C
+#define PCXHR_SELMIC_PHANTOM_ALIM      0x80
+
+
+static const unsigned char g_hr222_p_level[] = {
+    0x00,   /* [000] -49.5 dB: AKM[000] = -1.#INF dB   (mute) */
+    0x01,   /* [001] -49.0 dB: AKM[001] = -48.131 dB   (diff=0.86920 dB) */
+    0x01,   /* [002] -48.5 dB: AKM[001] = -48.131 dB   (diff=0.36920 dB) */
+    0x01,   /* [003] -48.0 dB: AKM[001] = -48.131 dB   (diff=0.13080 dB) */
+    0x01,   /* [004] -47.5 dB: AKM[001] = -48.131 dB   (diff=0.63080 dB) */
+    0x01,   /* [005] -46.5 dB: AKM[001] = -48.131 dB   (diff=1.63080 dB) */
+    0x01,   /* [006] -47.0 dB: AKM[001] = -48.131 dB   (diff=1.13080 dB) */
+    0x01,   /* [007] -46.0 dB: AKM[001] = -48.131 dB   (diff=2.13080 dB) */
+    0x01,   /* [008] -45.5 dB: AKM[001] = -48.131 dB   (diff=2.63080 dB) */
+    0x02,   /* [009] -45.0 dB: AKM[002] = -42.110 dB   (diff=2.88980 dB) */
+    0x02,   /* [010] -44.5 dB: AKM[002] = -42.110 dB   (diff=2.38980 dB) */
+    0x02,   /* [011] -44.0 dB: AKM[002] = -42.110 dB   (diff=1.88980 dB) */
+    0x02,   /* [012] -43.5 dB: AKM[002] = -42.110 dB   (diff=1.38980 dB) */
+    0x02,   /* [013] -43.0 dB: AKM[002] = -42.110 dB   (diff=0.88980 dB) */
+    0x02,   /* [014] -42.5 dB: AKM[002] = -42.110 dB   (diff=0.38980 dB) */
+    0x02,   /* [015] -42.0 dB: AKM[002] = -42.110 dB   (diff=0.11020 dB) */
+    0x02,   /* [016] -41.5 dB: AKM[002] = -42.110 dB   (diff=0.61020 dB) */
+    0x02,   /* [017] -41.0 dB: AKM[002] = -42.110 dB   (diff=1.11020 dB) */
+    0x02,   /* [018] -40.5 dB: AKM[002] = -42.110 dB   (diff=1.61020 dB) */
+    0x03,   /* [019] -40.0 dB: AKM[003] = -38.588 dB   (diff=1.41162 dB) */
+    0x03,   /* [020] -39.5 dB: AKM[003] = -38.588 dB   (diff=0.91162 dB) */
+    0x03,   /* [021] -39.0 dB: AKM[003] = -38.588 dB   (diff=0.41162 dB) */
+    0x03,   /* [022] -38.5 dB: AKM[003] = -38.588 dB   (diff=0.08838 dB) */
+    0x03,   /* [023] -38.0 dB: AKM[003] = -38.588 dB   (diff=0.58838 dB) */
+    0x03,   /* [024] -37.5 dB: AKM[003] = -38.588 dB   (diff=1.08838 dB) */
+    0x04,   /* [025] -37.0 dB: AKM[004] = -36.090 dB   (diff=0.91040 dB) */
+    0x04,   /* [026] -36.5 dB: AKM[004] = -36.090 dB   (diff=0.41040 dB) */
+    0x04,   /* [027] -36.0 dB: AKM[004] = -36.090 dB   (diff=0.08960 dB) */
+    0x04,   /* [028] -35.5 dB: AKM[004] = -36.090 dB   (diff=0.58960 dB) */
+    0x05,   /* [029] -35.0 dB: AKM[005] = -34.151 dB   (diff=0.84860 dB) */
+    0x05,   /* [030] -34.5 dB: AKM[005] = -34.151 dB   (diff=0.34860 dB) */
+    0x05,   /* [031] -34.0 dB: AKM[005] = -34.151 dB   (diff=0.15140 dB) */
+    0x05,   /* [032] -33.5 dB: AKM[005] = -34.151 dB   (diff=0.65140 dB) */
+    0x06,   /* [033] -33.0 dB: AKM[006] = -32.568 dB   (diff=0.43222 dB) */
+    0x06,   /* [034] -32.5 dB: AKM[006] = -32.568 dB   (diff=0.06778 dB) */
+    0x06,   /* [035] -32.0 dB: AKM[006] = -32.568 dB   (diff=0.56778 dB) */
+    0x07,   /* [036] -31.5 dB: AKM[007] = -31.229 dB   (diff=0.27116 dB) */
+    0x07,   /* [037] -31.0 dB: AKM[007] = -31.229 dB   (diff=0.22884 dB) */
+    0x08,   /* [038] -30.5 dB: AKM[008] = -30.069 dB   (diff=0.43100 dB) */
+    0x08,   /* [039] -30.0 dB: AKM[008] = -30.069 dB   (diff=0.06900 dB) */
+    0x09,   /* [040] -29.5 dB: AKM[009] = -29.046 dB   (diff=0.45405 dB) */
+    0x09,   /* [041] -29.0 dB: AKM[009] = -29.046 dB   (diff=0.04595 dB) */
+    0x0a,   /* [042] -28.5 dB: AKM[010] = -28.131 dB   (diff=0.36920 dB) */
+    0x0a,   /* [043] -28.0 dB: AKM[010] = -28.131 dB   (diff=0.13080 dB) */
+    0x0b,   /* [044] -27.5 dB: AKM[011] = -27.303 dB   (diff=0.19705 dB) */
+    0x0b,   /* [045] -27.0 dB: AKM[011] = -27.303 dB   (diff=0.30295 dB) */
+    0x0c,   /* [046] -26.5 dB: AKM[012] = -26.547 dB   (diff=0.04718 dB) */
+    0x0d,   /* [047] -26.0 dB: AKM[013] = -25.852 dB   (diff=0.14806 dB) */
+    0x0e,   /* [048] -25.5 dB: AKM[014] = -25.208 dB   (diff=0.29176 dB) */
+    0x0e,   /* [049] -25.0 dB: AKM[014] = -25.208 dB   (diff=0.20824 dB) */
+    0x0f,   /* [050] -24.5 dB: AKM[015] = -24.609 dB   (diff=0.10898 dB) */
+    0x10,   /* [051] -24.0 dB: AKM[016] = -24.048 dB   (diff=0.04840 dB) */
+    0x11,   /* [052] -23.5 dB: AKM[017] = -23.522 dB   (diff=0.02183 dB) */
+    0x12,   /* [053] -23.0 dB: AKM[018] = -23.025 dB   (diff=0.02535 dB) */
+    0x13,   /* [054] -22.5 dB: AKM[019] = -22.556 dB   (diff=0.05573 dB) */
+    0x14,   /* [055] -22.0 dB: AKM[020] = -22.110 dB   (diff=0.11020 dB) */
+    0x15,   /* [056] -21.5 dB: AKM[021] = -21.686 dB   (diff=0.18642 dB) */
+    0x17,   /* [057] -21.0 dB: AKM[023] = -20.896 dB   (diff=0.10375 dB) */
+    0x18,   /* [058] -20.5 dB: AKM[024] = -20.527 dB   (diff=0.02658 dB) */
+    0x1a,   /* [059] -20.0 dB: AKM[026] = -19.831 dB   (diff=0.16866 dB) */
+    0x1b,   /* [060] -19.5 dB: AKM[027] = -19.504 dB   (diff=0.00353 dB) */
+    0x1d,   /* [061] -19.0 dB: AKM[029] = -18.883 dB   (diff=0.11716 dB) */
+    0x1e,   /* [062] -18.5 dB: AKM[030] = -18.588 dB   (diff=0.08838 dB) */
+    0x20,   /* [063] -18.0 dB: AKM[032] = -18.028 dB   (diff=0.02780 dB) */
+    0x22,   /* [064] -17.5 dB: AKM[034] = -17.501 dB   (diff=0.00123 dB) */
+    0x24,   /* [065] -17.0 dB: AKM[036] = -17.005 dB   (diff=0.00475 dB) */
+    0x26,   /* [066] -16.5 dB: AKM[038] = -16.535 dB   (diff=0.03513 dB) */
+    0x28,   /* [067] -16.0 dB: AKM[040] = -16.090 dB   (diff=0.08960 dB) */
+    0x2b,   /* [068] -15.5 dB: AKM[043] = -15.461 dB   (diff=0.03857 dB) */
+    0x2d,   /* [069] -15.0 dB: AKM[045] = -15.067 dB   (diff=0.06655 dB) */
+    0x30,   /* [070] -14.5 dB: AKM[048] = -14.506 dB   (diff=0.00598 dB) */
+    0x33,   /* [071] -14.0 dB: AKM[051] = -13.979 dB   (diff=0.02060 dB) */
+    0x36,   /* [072] -13.5 dB: AKM[054] = -13.483 dB   (diff=0.01707 dB) */
+    0x39,   /* [073] -13.0 dB: AKM[057] = -13.013 dB   (diff=0.01331 dB) */
+    0x3c,   /* [074] -12.5 dB: AKM[060] = -12.568 dB   (diff=0.06778 dB) */
+    0x40,   /* [075] -12.0 dB: AKM[064] = -12.007 dB   (diff=0.00720 dB) */
+    0x44,   /* [076] -11.5 dB: AKM[068] = -11.481 dB   (diff=0.01937 dB) */
+    0x48,   /* [077] -11.0 dB: AKM[072] = -10.984 dB   (diff=0.01585 dB) */
+    0x4c,   /* [078] -10.5 dB: AKM[076] = -10.515 dB   (diff=0.01453 dB) */
+    0x51,   /* [079] -10.0 dB: AKM[081] = -9.961 dB    (diff=0.03890 dB) */
+    0x55,   /* [080] -9.5 dB:  AKM[085] = -9.542 dB    (diff=0.04243 dB) */
+    0x5a,   /* [081] -9.0 dB:  AKM[090] = -9.046 dB    (diff=0.04595 dB) */
+    0x60,   /* [082] -8.5 dB:  AKM[096] = -8.485 dB    (diff=0.01462 dB) */
+    0x66,   /* [083] -8.0 dB:  AKM[102] = -7.959 dB    (diff=0.04120 dB) */
+    0x6c,   /* [084] -7.5 dB:  AKM[108] = -7.462 dB    (diff=0.03767 dB) */
+    0x72,   /* [085] -7.0 dB:  AKM[114] = -6.993 dB    (diff=0.00729 dB) */
+    0x79,   /* [086] -6.5 dB:  AKM[121] = -6.475 dB    (diff=0.02490 dB) */
+    0x80,   /* [087] -6.0 dB:  AKM[128] = -5.987 dB    (diff=0.01340 dB) */
+    0x87,   /* [088] -5.5 dB:  AKM[135] = -5.524 dB    (diff=0.02413 dB) */
+    0x8f,   /* [089] -5.0 dB:  AKM[143] = -5.024 dB    (diff=0.02408 dB) */
+    0x98,   /* [090] -4.5 dB:  AKM[152] = -4.494 dB    (diff=0.00607 dB) */
+    0xa1,   /* [091] -4.0 dB:  AKM[161] = -3.994 dB    (diff=0.00571 dB) */
+    0xaa,   /* [092] -3.5 dB:  AKM[170] = -3.522 dB    (diff=0.02183 dB) */
+    0xb5,   /* [093] -3.0 dB:  AKM[181] = -2.977 dB    (diff=0.02277 dB) */
+    0xbf,   /* [094] -2.5 dB:  AKM[191] = -2.510 dB    (diff=0.01014 dB) */
+    0xcb,   /* [095] -2.0 dB:  AKM[203] = -1.981 dB    (diff=0.01912 dB) */
+    0xd7,   /* [096] -1.5 dB:  AKM[215] = -1.482 dB    (diff=0.01797 dB) */
+    0xe3,   /* [097] -1.0 dB:  AKM[227] = -1.010 dB    (diff=0.01029 dB) */
+    0xf1,   /* [098] -0.5 dB:  AKM[241] = -0.490 dB    (diff=0.00954 dB) */
+    0xff,   /* [099] +0.0 dB:  AKM[255] = +0.000 dB    (diff=0.00000 dB) */
+};
+
+
+static void hr222_config_akm(struct pcxhr_mgr *mgr, unsigned short data)
+{
+       unsigned short mask = 0x8000;
+       /* activate access to codec registers */
+       PCXHR_INPB(mgr, PCXHR_XLX_HIFREQ);
+
+       while (mask) {
+               PCXHR_OUTPB(mgr, PCXHR_XLX_DATA,
+                           data & mask ? PCXHR_DATA_CODEC : 0);
+               mask >>= 1;
+       }
+       /* termiate access to codec registers */
+       PCXHR_INPB(mgr, PCXHR_XLX_RUER);
+}
+
+
+static int hr222_set_hw_playback_level(struct pcxhr_mgr *mgr,
+                                      int idx, int level)
+{
+       unsigned short cmd;
+       if (idx > 1 ||
+           level < 0 ||
+           level >= ARRAY_SIZE(g_hr222_p_level))
+               return -EINVAL;
+
+       if (idx == 0)
+               cmd = AKM_LEFT_LEVEL_CMD;
+       else
+               cmd = AKM_RIGHT_LEVEL_CMD;
+
+       /* conversion from PmBoardCodedLevel to AKM nonlinear programming */
+       cmd += g_hr222_p_level[level];
+
+       hr222_config_akm(mgr, cmd);
+       return 0;
+}
+
+
+static int hr222_set_hw_capture_level(struct pcxhr_mgr *mgr,
+                                     int level_l, int level_r, int level_mic)
+{
+       /* program all input levels at the same time */
+       unsigned int data;
+       int i;
+
+       if (!mgr->capture_chips)
+               return -EINVAL; /* no PCX22 */
+
+       data  = ((level_mic & 0xff) << 24);     /* micro is mono, but apply */
+       data |= ((level_mic & 0xff) << 16);     /* level on both channels */
+       data |= ((level_r & 0xff) << 8);        /* line input right channel */
+       data |= (level_l & 0xff);               /* line input left channel */
+
+       PCXHR_INPB(mgr, PCXHR_XLX_DATA);        /* activate input codec */
+       /* send 32 bits (4 x 8 bits) */
+       for (i = 0; i < 32; i++, data <<= 1) {
+               PCXHR_OUTPB(mgr, PCXHR_XLX_DATA,
+                           (data & 0x80000000) ? PCXHR_DATA_CODEC : 0);
+       }
+       PCXHR_INPB(mgr, PCXHR_XLX_RUER);        /* close input level codec */
+       return 0;
+}
+
+static void hr222_micro_boost(struct pcxhr_mgr *mgr, int level);
+
+int hr222_sub_init(struct pcxhr_mgr *mgr)
+{
+       unsigned char reg;
+
+       mgr->board_has_analog = 1;      /* analog always available */
+       mgr->xlx_cfg = PCXHR_CFG_SYNCDSP_MASK;
+
+       reg = PCXHR_INPB(mgr, PCXHR_XLX_STATUS);
+       if (reg & PCXHR_STAT_MIC_CAPS)
+               mgr->board_has_mic = 1; /* microphone available */
+       snd_printdd("MIC input available = %d\n", mgr->board_has_mic);
+
+       /* reset codec */
+       PCXHR_OUTPB(mgr, PCXHR_DSP_RESET,
+                   PCXHR_DSP_RESET_DSP);
+       msleep(5);
+       PCXHR_OUTPB(mgr, PCXHR_DSP_RESET,
+                   PCXHR_DSP_RESET_DSP  |
+                   PCXHR_DSP_RESET_MUTE |
+                   PCXHR_DSP_RESET_CODEC);
+       msleep(5);
+
+       /* config AKM */
+       hr222_config_akm(mgr, AKM_POWER_CONTROL_CMD);
+       hr222_config_akm(mgr, AKM_CLOCK_INF_55K_CMD);
+       hr222_config_akm(mgr, AKM_UNMUTE_CMD);
+       hr222_config_akm(mgr, AKM_RESET_OFF_CMD);
+
+       /* init micro boost */
+       hr222_micro_boost(mgr, 0);
+
+       return 0;
+}
+
+
+/* calc PLL register */
+/* TODO : there is a very similar fct in pcxhr.c */
+static int hr222_pll_freq_register(unsigned int freq,
+                                  unsigned int *pllreg,
+                                  unsigned int *realfreq)
+{
+       unsigned int reg;
+
+       if (freq < 6900 || freq > 219000)
+               return -EINVAL;
+       reg = (28224000 * 2) / freq;
+       reg = (reg - 1) / 2;
+       if (reg < 0x100)
+               *pllreg = reg + 0xC00;
+       else if (reg < 0x200)
+               *pllreg = reg + 0x800;
+       else if (reg < 0x400)
+               *pllreg = reg & 0x1ff;
+       else if (reg < 0x800) {
+               *pllreg = ((reg >> 1) & 0x1ff) + 0x200;
+               reg &= ~1;
+       } else {
+               *pllreg = ((reg >> 2) & 0x1ff) + 0x400;
+               reg &= ~3;
+       }
+       if (realfreq)
+               *realfreq = (28224000 / (reg + 1));
+       return 0;
+}
+
+int hr222_sub_set_clock(struct pcxhr_mgr *mgr,
+                       unsigned int rate,
+                       int *changed)
+{
+       unsigned int speed, pllreg = 0;
+       int err;
+       unsigned realfreq = rate;
+
+       switch (mgr->use_clock_type) {
+       case HR22_CLOCK_TYPE_INTERNAL:
+               err = hr222_pll_freq_register(rate, &pllreg, &realfreq);
+               if (err)
+                       return err;
+
+               mgr->xlx_cfg &= ~(PCXHR_CFG_CLOCKIN_SEL_MASK |
+                                 PCXHR_CFG_CLOCK_UER1_SEL_MASK);
+               break;
+       case HR22_CLOCK_TYPE_AES_SYNC:
+               mgr->xlx_cfg |= PCXHR_CFG_CLOCKIN_SEL_MASK;
+               mgr->xlx_cfg &= ~PCXHR_CFG_CLOCK_UER1_SEL_MASK;
+               break;
+       case HR22_CLOCK_TYPE_AES_1:
+               if (!mgr->board_has_aes1)
+                       return -EINVAL;
+
+               mgr->xlx_cfg |= (PCXHR_CFG_CLOCKIN_SEL_MASK |
+                                PCXHR_CFG_CLOCK_UER1_SEL_MASK);
+               break;
+       default:
+               return -EINVAL;
+       }
+       hr222_config_akm(mgr, AKM_MUTE_CMD);
+
+       if (mgr->use_clock_type == HR22_CLOCK_TYPE_INTERNAL) {
+               PCXHR_OUTPB(mgr, PCXHR_XLX_HIFREQ, pllreg >> 8);
+               PCXHR_OUTPB(mgr, PCXHR_XLX_LOFREQ, pllreg & 0xff);
+       }
+
+       /* set clock source */
+       PCXHR_OUTPB(mgr, PCXHR_XLX_CFG, mgr->xlx_cfg);
+
+       /* codec speed modes */
+       speed = rate < 55000 ? 0 : 1;
+       if (mgr->codec_speed != speed) {
+               mgr->codec_speed = speed;
+               if (speed == 0)
+                       hr222_config_akm(mgr, AKM_CLOCK_INF_55K_CMD);
+               else
+                       hr222_config_akm(mgr, AKM_CLOCK_SUP_55K_CMD);
+       }
+
+       mgr->sample_rate_real = realfreq;
+       mgr->cur_clock_type = mgr->use_clock_type;
+
+       if (changed)
+               *changed = 1;
+
+       hr222_config_akm(mgr, AKM_UNMUTE_CMD);
+
+       snd_printdd("set_clock to %dHz (realfreq=%d pllreg=%x)\n",
+                   rate, realfreq, pllreg);
+       return 0;
+}
+
+int hr222_get_external_clock(struct pcxhr_mgr *mgr,
+                            enum pcxhr_clock_type clock_type,
+                            int *sample_rate)
+{
+       int rate, calc_rate = 0;
+       unsigned int ticks;
+       unsigned char mask, reg;
+
+       if (clock_type == HR22_CLOCK_TYPE_AES_SYNC) {
+
+               mask = (PCXHR_SUER_CLOCK_PRESENT_MASK |
+                       PCXHR_SUER_DATA_PRESENT_MASK);
+               reg = PCXHR_STAT_FREQ_SYNC_MASK;
+
+       } else if (clock_type == HR22_CLOCK_TYPE_AES_1 && mgr->board_has_aes1) {
+
+               mask = (PCXHR_SUER1_CLOCK_PRESENT_MASK |
+                       PCXHR_SUER1_DATA_PRESENT_MASK);
+               reg = PCXHR_STAT_FREQ_UER1_MASK;
+
+       } else {
+               snd_printdd("get_external_clock : type %d not supported\n",
+                           clock_type);
+               return -EINVAL; /* other clocks not supported */
+       }
+
+       if ((PCXHR_INPB(mgr, PCXHR_XLX_CSUER) & mask) != mask) {
+               snd_printdd("get_external_clock(%d) = 0 Hz\n", clock_type);
+               *sample_rate = 0;
+               return 0; /* no external clock locked */
+       }
+
+       PCXHR_OUTPB(mgr, PCXHR_XLX_STATUS, reg); /* calculate freq */
+
+       /* save the measured clock frequency */
+       reg |= PCXHR_STAT_FREQ_SAVE_MASK;
+
+       if (mgr->last_reg_stat != reg) {
+               udelay(500);    /* wait min 2 cycles of lowest freq (8000) */
+               mgr->last_reg_stat = reg;
+       }
+
+       PCXHR_OUTPB(mgr, PCXHR_XLX_STATUS, reg); /* save */
+
+       /* get the frequency */
+       ticks = (unsigned int)PCXHR_INPB(mgr, PCXHR_XLX_CFG);
+       ticks = (ticks & 0x03) << 8;
+       ticks |= (unsigned int)PCXHR_INPB(mgr, PCXHR_DSP_RESET);
+
+       if (ticks != 0)
+               calc_rate = 28224000 / ticks;
+       /* rounding */
+       if (calc_rate > 184200)
+               rate = 192000;
+       else if (calc_rate > 152200)
+               rate = 176400;
+       else if (calc_rate > 112000)
+               rate = 128000;
+       else if (calc_rate > 92100)
+               rate = 96000;
+       else if (calc_rate > 76100)
+               rate = 88200;
+       else if (calc_rate > 56000)
+               rate = 64000;
+       else if (calc_rate > 46050)
+               rate = 48000;
+       else if (calc_rate > 38050)
+               rate = 44100;
+       else if (calc_rate > 28000)
+               rate = 32000;
+       else if (calc_rate > 23025)
+               rate = 24000;
+       else if (calc_rate > 19025)
+               rate = 22050;
+       else if (calc_rate > 14000)
+               rate = 16000;
+       else if (calc_rate > 11512)
+               rate = 12000;
+       else if (calc_rate > 9512)
+               rate = 11025;
+       else if (calc_rate > 7000)
+               rate = 8000;
+       else
+               rate = 0;
+
+       snd_printdd("External clock is at %d Hz (measured %d Hz)\n",
+                   rate, calc_rate);
+       *sample_rate = rate;
+       return 0;
+}
+
+
+int hr222_update_analog_audio_level(struct snd_pcxhr *chip,
+                                   int is_capture, int channel)
+{
+       snd_printdd("hr222_update_analog_audio_level(%s chan=%d)\n",
+                   is_capture ? "capture" : "playback", channel);
+       if (is_capture) {
+               int level_l, level_r, level_mic;
+               /* we have to update all levels */
+               if (chip->analog_capture_active) {
+                       level_l = chip->analog_capture_volume[0];
+                       level_r = chip->analog_capture_volume[1];
+               } else {
+                       level_l = HR222_LINE_CAPTURE_LEVEL_MIN;
+                       level_r = HR222_LINE_CAPTURE_LEVEL_MIN;
+               }
+               if (chip->mic_active)
+                       level_mic = chip->mic_volume;
+               else
+                       level_mic = HR222_MICRO_CAPTURE_LEVEL_MIN;
+               return hr222_set_hw_capture_level(chip->mgr,
+                                                level_l, level_r, level_mic);
+       } else {
+               int vol;
+               if (chip->analog_playback_active[channel])
+                       vol = chip->analog_playback_volume[channel];
+               else
+                       vol = HR222_LINE_PLAYBACK_LEVEL_MIN;
+               return hr222_set_hw_playback_level(chip->mgr, channel, vol);
+       }
+}
+
+
+/*texts[5] = {"Line", "Digital", "Digi+SRC", "Mic", "Line+Mic"}*/
+#define SOURCE_LINE    0
+#define SOURCE_DIGITAL 1
+#define SOURCE_DIGISRC 2
+#define SOURCE_MIC     3
+#define SOURCE_LINEMIC 4
+
+int hr222_set_audio_source(struct snd_pcxhr *chip)
+{
+       int digital = 0;
+       /* default analog source */
+       chip->mgr->xlx_cfg &= ~(PCXHR_CFG_SRC_MASK |
+                               PCXHR_CFG_DATAIN_SEL_MASK |
+                               PCXHR_CFG_DATA_UER1_SEL_MASK);
+
+       if (chip->audio_capture_source == SOURCE_DIGISRC) {
+               chip->mgr->xlx_cfg |= PCXHR_CFG_SRC_MASK;
+               digital = 1;
+       } else {
+               if (chip->audio_capture_source == SOURCE_DIGITAL)
+                       digital = 1;
+       }
+       if (digital) {
+               chip->mgr->xlx_cfg |=  PCXHR_CFG_DATAIN_SEL_MASK;
+               if (chip->mgr->board_has_aes1) {
+                       /* get data from the AES1 plug */
+                       chip->mgr->xlx_cfg |= PCXHR_CFG_DATA_UER1_SEL_MASK;
+               }
+               /* chip->mic_active = 0; */
+               /* chip->analog_capture_active = 0; */
+       } else {
+               int update_lvl = 0;
+               chip->analog_capture_active = 0;
+               chip->mic_active = 0;
+               if (chip->audio_capture_source == SOURCE_LINE ||
+                   chip->audio_capture_source == SOURCE_LINEMIC) {
+                       if (chip->analog_capture_active == 0)
+                               update_lvl = 1;
+                       chip->analog_capture_active = 1;
+               }
+               if (chip->audio_capture_source == SOURCE_MIC ||
+                   chip->audio_capture_source == SOURCE_LINEMIC) {
+                       if (chip->mic_active == 0)
+                               update_lvl = 1;
+                       chip->mic_active = 1;
+               }
+               if (update_lvl) {
+                       /* capture: update all 3 mutes/unmutes with one call */
+                       hr222_update_analog_audio_level(chip, 1, 0);
+               }
+       }
+       /* set the source infos (max 3 bits modified) */
+       PCXHR_OUTPB(chip->mgr, PCXHR_XLX_CFG, chip->mgr->xlx_cfg);
+       return 0;
+}
+
+
+int hr222_iec958_capture_byte(struct snd_pcxhr *chip,
+                            int aes_idx, unsigned char *aes_bits)
+{
+       unsigned char idx = (unsigned char)(aes_idx * 8);
+       unsigned char temp = 0;
+       unsigned char mask = chip->mgr->board_has_aes1 ?
+               PCXHR_SUER1_BIT_C_READ_MASK : PCXHR_SUER_BIT_C_READ_MASK;
+       int i;
+       for (i = 0; i < 8; i++) {
+               PCXHR_OUTPB(chip->mgr, PCXHR_XLX_RUER, idx++); /* idx < 192 */
+               temp <<= 1;
+               if (PCXHR_INPB(chip->mgr, PCXHR_XLX_CSUER) & mask)
+                       temp |= 1;
+       }
+       snd_printdd("read iec958 AES %d byte %d = 0x%x\n",
+                   chip->chip_idx, aes_idx, temp);
+       *aes_bits = temp;
+       return 0;
+}
+
+
+int hr222_iec958_update_byte(struct snd_pcxhr *chip,
+                            int aes_idx, unsigned char aes_bits)
+{
+       int i;
+       unsigned char new_bits = aes_bits;
+       unsigned char old_bits = chip->aes_bits[aes_idx];
+       unsigned char idx = (unsigned char)(aes_idx * 8);
+       for (i = 0; i < 8; i++) {
+               if ((old_bits & 0x01) != (new_bits & 0x01)) {
+                       /* idx < 192 */
+                       PCXHR_OUTPB(chip->mgr, PCXHR_XLX_RUER, idx);
+                       /* write C and U bit */
+                       PCXHR_OUTPB(chip->mgr, PCXHR_XLX_CSUER, new_bits&0x01 ?
+                                   PCXHR_SUER_BIT_C_WRITE_MASK : 0);
+               }
+               idx++;
+               old_bits >>= 1;
+               new_bits >>= 1;
+       }
+       chip->aes_bits[aes_idx] = aes_bits;
+       return 0;
+}
+
+static void hr222_micro_boost(struct pcxhr_mgr *mgr, int level)
+{
+       unsigned char boost_mask;
+       boost_mask = (unsigned char) (level << PCXHR_SELMIC_PREAMPLI_OFFSET);
+       if (boost_mask & (~PCXHR_SELMIC_PREAMPLI_MASK))
+               return; /* only values form 0 to 3 accepted */
+
+       mgr->xlx_selmic &= ~PCXHR_SELMIC_PREAMPLI_MASK;
+       mgr->xlx_selmic |= boost_mask;
+
+       PCXHR_OUTPB(mgr, PCXHR_XLX_SELMIC, mgr->xlx_selmic);
+
+       snd_printdd("hr222_micro_boost : set %x\n", boost_mask);
+}
+
+static void hr222_phantom_power(struct pcxhr_mgr *mgr, int power)
+{
+       if (power)
+               mgr->xlx_selmic |= PCXHR_SELMIC_PHANTOM_ALIM;
+       else
+               mgr->xlx_selmic &= ~PCXHR_SELMIC_PHANTOM_ALIM;
+
+       PCXHR_OUTPB(mgr, PCXHR_XLX_SELMIC, mgr->xlx_selmic);
+
+       snd_printdd("hr222_phantom_power : set %d\n", power);
+}
+
+
+/* mic level */
+static const DECLARE_TLV_DB_SCALE(db_scale_mic_hr222, -9850, 50, 650);
+
+static int hr222_mic_vol_info(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = HR222_MICRO_CAPTURE_LEVEL_MIN; /* -98 dB */
+       /* gains from 9 dB to 31.5 dB not recommended; use micboost instead */
+       uinfo->value.integer.max = HR222_MICRO_CAPTURE_LEVEL_MAX; /*  +7 dB */
+       return 0;
+}
+
+static int hr222_mic_vol_get(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
+       mutex_lock(&chip->mgr->mixer_mutex);
+       ucontrol->value.integer.value[0] = chip->mic_volume;
+       mutex_unlock(&chip->mgr->mixer_mutex);
+       return 0;
+}
+
+static int hr222_mic_vol_put(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
+       int changed = 0;
+       mutex_lock(&chip->mgr->mixer_mutex);
+       if (chip->mic_volume != ucontrol->value.integer.value[0]) {
+               changed = 1;
+               chip->mic_volume = ucontrol->value.integer.value[0];
+               hr222_update_analog_audio_level(chip, 1, 0);
+       }
+       mutex_unlock(&chip->mgr->mixer_mutex);
+       return changed;
+}
+
+static struct snd_kcontrol_new hr222_control_mic_level = {
+       .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+       .access =       (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                        SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+       .name =         "Mic Capture Volume",
+       .info =         hr222_mic_vol_info,
+       .get =          hr222_mic_vol_get,
+       .put =          hr222_mic_vol_put,
+       .tlv = { .p = db_scale_mic_hr222 },
+};
+
+
+/* mic boost level */
+static const DECLARE_TLV_DB_SCALE(db_scale_micboost_hr222, 0, 1800, 5400);
+
+static int hr222_mic_boost_info(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;   /*  0 dB */
+       uinfo->value.integer.max = 3;   /* 54 dB */
+       return 0;
+}
+
+static int hr222_mic_boost_get(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
+       mutex_lock(&chip->mgr->mixer_mutex);
+       ucontrol->value.integer.value[0] = chip->mic_boost;
+       mutex_unlock(&chip->mgr->mixer_mutex);
+       return 0;
+}
+
+static int hr222_mic_boost_put(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
+       int changed = 0;
+       mutex_lock(&chip->mgr->mixer_mutex);
+       if (chip->mic_boost != ucontrol->value.integer.value[0]) {
+               changed = 1;
+               chip->mic_boost = ucontrol->value.integer.value[0];
+               hr222_micro_boost(chip->mgr, chip->mic_boost);
+       }
+       mutex_unlock(&chip->mgr->mixer_mutex);
+       return changed;
+}
+
+static struct snd_kcontrol_new hr222_control_mic_boost = {
+       .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+       .access =       (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                        SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+       .name =         "MicBoost Capture Volume",
+       .info =         hr222_mic_boost_info,
+       .get =          hr222_mic_boost_get,
+       .put =          hr222_mic_boost_put,
+       .tlv = { .p = db_scale_micboost_hr222 },
+};
+
+
+/******************* Phantom power switch *******************/
+#define hr222_phantom_power_info       snd_ctl_boolean_mono_info
+
+static int hr222_phantom_power_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
+       mutex_lock(&chip->mgr->mixer_mutex);
+       ucontrol->value.integer.value[0] = chip->phantom_power;
+       mutex_unlock(&chip->mgr->mixer_mutex);
+       return 0;
+}
+
+static int hr222_phantom_power_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
+       int power, changed = 0;
+
+       mutex_lock(&chip->mgr->mixer_mutex);
+       power = !!ucontrol->value.integer.value[0];
+       if (chip->phantom_power != power) {
+               hr222_phantom_power(chip->mgr, power);
+               chip->phantom_power = power;
+               changed = 1;
+       }
+       mutex_unlock(&chip->mgr->mixer_mutex);
+       return changed;
+}
+
+static struct snd_kcontrol_new hr222_phantom_power_switch = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Phantom Power Switch",
+       .info = hr222_phantom_power_info,
+       .get = hr222_phantom_power_get,
+       .put = hr222_phantom_power_put,
+};
+
+
+int hr222_add_mic_controls(struct snd_pcxhr *chip)
+{
+       int err;
+       if (!chip->mgr->board_has_mic)
+               return 0;
+
+       /* controls */
+       err = snd_ctl_add(chip->card, snd_ctl_new1(&hr222_control_mic_level,
+                                                  chip));
+       if (err < 0)
+               return err;
+
+       err = snd_ctl_add(chip->card, snd_ctl_new1(&hr222_control_mic_boost,
+                                                  chip));
+       if (err < 0)
+               return err;
+
+       err = snd_ctl_add(chip->card, snd_ctl_new1(&hr222_phantom_power_switch,
+                                                  chip));
+       return err;
+}
diff --git a/sound/pci/pcxhr/pcxhr_mix22.h b/sound/pci/pcxhr/pcxhr_mix22.h
new file mode 100644 (file)
index 0000000..6b318b2
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Driver for Digigram pcxhr compatible soundcards
+ *
+ * low level interface with interrupt ans message handling
+ *
+ * Copyright (c) 2004 by Digigram <alsa@digigram.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __SOUND_PCXHR_MIX22_H
+#define __SOUND_PCXHR_MIX22_H
+
+struct pcxhr_mgr;
+
+int hr222_sub_init(struct pcxhr_mgr *mgr);
+int hr222_sub_set_clock(struct pcxhr_mgr *mgr, unsigned int rate,
+                       int *changed);
+int hr222_get_external_clock(struct pcxhr_mgr *mgr,
+                            enum pcxhr_clock_type clock_type,
+                            int *sample_rate);
+
+#define HR222_LINE_PLAYBACK_LEVEL_MIN          0       /* -25.5 dB */
+#define HR222_LINE_PLAYBACK_ZERO_LEVEL         51      /* 0.0 dB */
+#define HR222_LINE_PLAYBACK_LEVEL_MAX          99      /* +24.0 dB */
+
+#define HR222_LINE_CAPTURE_LEVEL_MIN           0       /* -111.5 dB */
+#define HR222_LINE_CAPTURE_ZERO_LEVEL          223     /* 0.0 dB */
+#define HR222_LINE_CAPTURE_LEVEL_MAX           255     /* +16 dB */
+#define HR222_MICRO_CAPTURE_LEVEL_MIN          0       /* -98.5 dB */
+#define HR222_MICRO_CAPTURE_LEVEL_MAX          210     /* +6.5 dB */
+
+int hr222_update_analog_audio_level(struct snd_pcxhr *chip,
+                                   int is_capture,
+                                   int channel);
+int hr222_set_audio_source(struct snd_pcxhr *chip);
+int hr222_iec958_capture_byte(struct snd_pcxhr *chip, int aes_idx,
+                             unsigned char *aes_bits);
+int hr222_iec958_update_byte(struct snd_pcxhr *chip, int aes_idx,
+                            unsigned char aes_bits);
+
+int hr222_add_mic_controls(struct snd_pcxhr *chip);
+
+#endif /* __SOUND_PCXHR_MIX22_H */
index aabc7bc..2436e37 100644 (file)
 #include <sound/tlv.h>
 #include <sound/asoundef.h>
 #include "pcxhr_mixer.h"
+#include "pcxhr_mix22.h"
 
+#define PCXHR_LINE_CAPTURE_LEVEL_MIN   0       /* -112.0 dB */
+#define PCXHR_LINE_CAPTURE_LEVEL_MAX   255     /* +15.5 dB */
+#define PCXHR_LINE_CAPTURE_ZERO_LEVEL  224     /* 0.0 dB ( 0 dBu -> 0 dBFS ) */
 
-#define PCXHR_ANALOG_CAPTURE_LEVEL_MIN   0     /* -96.0 dB */
-#define PCXHR_ANALOG_CAPTURE_LEVEL_MAX   255   /* +31.5 dB */
-#define PCXHR_ANALOG_CAPTURE_ZERO_LEVEL  224   /* +16.0 dB ( +31.5 dB - fix level +15.5 dB ) */
+#define PCXHR_LINE_PLAYBACK_LEVEL_MIN  0       /* -104.0 dB */
+#define PCXHR_LINE_PLAYBACK_LEVEL_MAX  128     /* +24.0 dB */
+#define PCXHR_LINE_PLAYBACK_ZERO_LEVEL 104     /* 0.0 dB ( 0 dBFS -> 0 dBu ) */
 
-#define PCXHR_ANALOG_PLAYBACK_LEVEL_MIN  0     /* -128.0 dB */
-#define PCXHR_ANALOG_PLAYBACK_LEVEL_MAX  128   /*    0.0 dB */
-#define PCXHR_ANALOG_PLAYBACK_ZERO_LEVEL 104   /*  -24.0 dB ( 0.0 dB - fix level +24.0 dB ) */
-
-static const DECLARE_TLV_DB_SCALE(db_scale_analog_capture, -9600, 50, 3150);
+static const DECLARE_TLV_DB_SCALE(db_scale_analog_capture, -11200, 50, 1550);
 static const DECLARE_TLV_DB_SCALE(db_scale_analog_playback, -10400, 100, 2400);
 
-static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip, int is_capture, int channel)
+static const DECLARE_TLV_DB_SCALE(db_scale_a_hr222_capture, -11150, 50, 1600);
+static const DECLARE_TLV_DB_SCALE(db_scale_a_hr222_playback, -2550, 50, 2400);
+
+static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip,
+                                          int is_capture, int channel)
 {
        int err, vol;
        struct pcxhr_rmh rmh;
@@ -60,15 +64,17 @@ static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip, int is_captur
                if (chip->analog_playback_active[channel])
                        vol = chip->analog_playback_volume[channel];
                else
-                       vol = PCXHR_ANALOG_PLAYBACK_LEVEL_MIN;
-               rmh.cmd[2] = PCXHR_ANALOG_PLAYBACK_LEVEL_MAX - vol;     /* playback analog levels are inversed */
+                       vol = PCXHR_LINE_PLAYBACK_LEVEL_MIN;
+               /* playback analog levels are inversed */
+               rmh.cmd[2] = PCXHR_LINE_PLAYBACK_LEVEL_MAX - vol;
        }
        rmh.cmd[1]  = 1 << ((2 * chip->chip_idx) + channel);    /* audio mask */
        rmh.cmd_len = 3;
        err = pcxhr_send_msg(chip->mgr, &rmh);
        if (err < 0) {
-               snd_printk(KERN_DEBUG "error update_analog_audio_level card(%d) "
-                          "is_capture(%d) err(%x)\n", chip->chip_idx, is_capture, err);
+               snd_printk(KERN_DEBUG "error update_analog_audio_level card(%d)"
+                          " is_capture(%d) err(%x)\n",
+                          chip->chip_idx, is_capture, err);
                return -EINVAL;
        }
        return 0;
@@ -80,14 +86,34 @@ static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip, int is_captur
 static int pcxhr_analog_vol_info(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_info *uinfo)
 {
+       struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
+
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
        uinfo->count = 2;
        if (kcontrol->private_value == 0) {     /* playback */
-               uinfo->value.integer.min = PCXHR_ANALOG_PLAYBACK_LEVEL_MIN;     /* -128 dB */
-               uinfo->value.integer.max = PCXHR_ANALOG_PLAYBACK_LEVEL_MAX;     /* 0 dB */
+           if (chip->mgr->is_hr_stereo) {
+               uinfo->value.integer.min =
+                       HR222_LINE_PLAYBACK_LEVEL_MIN;  /* -25 dB */
+               uinfo->value.integer.max =
+                       HR222_LINE_PLAYBACK_LEVEL_MAX;  /* +24 dB */
+           } else {
+               uinfo->value.integer.min =
+                       PCXHR_LINE_PLAYBACK_LEVEL_MIN;  /*-104 dB */
+               uinfo->value.integer.max =
+                       PCXHR_LINE_PLAYBACK_LEVEL_MAX;  /* +24 dB */
+           }
        } else {                                /* capture */
-               uinfo->value.integer.min = PCXHR_ANALOG_CAPTURE_LEVEL_MIN;      /* -96 dB */
-               uinfo->value.integer.max = PCXHR_ANALOG_CAPTURE_LEVEL_MAX;      /* 31.5 dB */
+           if (chip->mgr->is_hr_stereo) {
+               uinfo->value.integer.min =
+                       HR222_LINE_CAPTURE_LEVEL_MIN;   /*-112 dB */
+               uinfo->value.integer.max =
+                       HR222_LINE_CAPTURE_LEVEL_MAX;   /* +15.5 dB */
+           } else {
+               uinfo->value.integer.min =
+                       PCXHR_LINE_CAPTURE_LEVEL_MIN;   /*-112 dB */
+               uinfo->value.integer.max =
+                       PCXHR_LINE_CAPTURE_LEVEL_MAX;   /* +15.5 dB */
+           }
        }
        return 0;
 }
@@ -98,11 +124,11 @@ static int pcxhr_analog_vol_get(struct snd_kcontrol *kcontrol,
        struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
        mutex_lock(&chip->mgr->mixer_mutex);
        if (kcontrol->private_value == 0) {     /* playback */
-               ucontrol->value.integer.value[0] = chip->analog_playback_volume[0];
-               ucontrol->value.integer.value[1] = chip->analog_playback_volume[1];
+         ucontrol->value.integer.value[0] = chip->analog_playback_volume[0];
+         ucontrol->value.integer.value[1] = chip->analog_playback_volume[1];
        } else {                                /* capture */
-               ucontrol->value.integer.value[0] = chip->analog_capture_volume[0];
-               ucontrol->value.integer.value[1] = chip->analog_capture_volume[1];
+         ucontrol->value.integer.value[0] = chip->analog_capture_volume[0];
+         ucontrol->value.integer.value[1] = chip->analog_capture_volume[1];
        }
        mutex_unlock(&chip->mgr->mixer_mutex);
        return 0;
@@ -123,18 +149,35 @@ static int pcxhr_analog_vol_put(struct snd_kcontrol *kcontrol,
                        &chip->analog_capture_volume[i] :
                        &chip->analog_playback_volume[i];
                if (is_capture) {
-                       if (new_volume < PCXHR_ANALOG_CAPTURE_LEVEL_MIN ||
-                           new_volume > PCXHR_ANALOG_CAPTURE_LEVEL_MAX)
-                               continue;
+                       if (chip->mgr->is_hr_stereo) {
+                               if (new_volume < HR222_LINE_CAPTURE_LEVEL_MIN ||
+                                   new_volume > HR222_LINE_CAPTURE_LEVEL_MAX)
+                                       continue;
+                       } else {
+                               if (new_volume < PCXHR_LINE_CAPTURE_LEVEL_MIN ||
+                                   new_volume > PCXHR_LINE_CAPTURE_LEVEL_MAX)
+                                       continue;
+                       }
                } else {
-                       if (new_volume < PCXHR_ANALOG_PLAYBACK_LEVEL_MIN ||
-                           new_volume > PCXHR_ANALOG_PLAYBACK_LEVEL_MAX)
-                               continue;
+                       if (chip->mgr->is_hr_stereo) {
+                               if (new_volume < HR222_LINE_PLAYBACK_LEVEL_MIN ||
+                                   new_volume > HR222_LINE_PLAYBACK_LEVEL_MAX)
+                                       continue;
+                       } else {
+                               if (new_volume < PCXHR_LINE_PLAYBACK_LEVEL_MIN ||
+                                   new_volume > PCXHR_LINE_PLAYBACK_LEVEL_MAX)
+                                       continue;
+                       }
                }
                if (*stored_volume != new_volume) {
                        *stored_volume = new_volume;
                        changed = 1;
-                       pcxhr_update_analog_audio_level(chip, is_capture, i);
+                       if (chip->mgr->is_hr_stereo)
+                               hr222_update_analog_audio_level(chip,
+                                                               is_capture, i);
+                       else
+                               pcxhr_update_analog_audio_level(chip,
+                                                               is_capture, i);
                }
        }
        mutex_unlock(&chip->mgr->mixer_mutex);
@@ -153,6 +196,7 @@ static struct snd_kcontrol_new pcxhr_control_analog_level = {
 };
 
 /* shared */
+
 #define pcxhr_sw_info          snd_ctl_boolean_stereo_info
 
 static int pcxhr_audio_sw_get(struct snd_kcontrol *kcontrol,
@@ -180,7 +224,10 @@ static int pcxhr_audio_sw_put(struct snd_kcontrol *kcontrol,
                                !!ucontrol->value.integer.value[i];
                        changed = 1;
                        /* update playback levels */
-                       pcxhr_update_analog_audio_level(chip, 0, i);
+                       if (chip->mgr->is_hr_stereo)
+                               hr222_update_analog_audio_level(chip, 0, i);
+                       else
+                               pcxhr_update_analog_audio_level(chip, 0, i);
                }
        }
        mutex_unlock(&chip->mgr->mixer_mutex);
@@ -251,7 +298,8 @@ static int pcxhr_update_playback_stream_level(struct snd_pcxhr* chip, int idx)
 #define VALID_AUDIO_IO_MUTE_LEVEL      0x000004
 #define VALID_AUDIO_IO_MUTE_MONITOR_1  0x000008
 
-static int pcxhr_update_audio_pipe_level(struct snd_pcxhr* chip, int capture, int channel)
+static int pcxhr_update_audio_pipe_level(struct snd_pcxhr *chip,
+                                        int capture, int channel)
 {
        int err;
        struct pcxhr_rmh rmh;
@@ -264,18 +312,20 @@ static int pcxhr_update_audio_pipe_level(struct snd_pcxhr* chip, int capture, in
 
        pcxhr_init_rmh(&rmh, CMD_AUDIO_LEVEL_ADJUST);
        /* add channel mask */
-       pcxhr_set_pipe_cmd_params(&rmh, capture, 0, 0, 1 << (channel + pipe->first_audio));
-       /* TODO : if mask (3 << pipe->first_audio) is used, left and right channel
-        * will be programmed to the same params
-        */
+       pcxhr_set_pipe_cmd_params(&rmh, capture, 0, 0,
+                                 1 << (channel + pipe->first_audio));
+       /* TODO : if mask (3 << pipe->first_audio) is used, left and right
+        * channel will be programmed to the same params */
        if (capture) {
                rmh.cmd[0] |= VALID_AUDIO_IO_DIGITAL_LEVEL;
-               /* VALID_AUDIO_IO_MUTE_LEVEL not yet handled (capture pipe level) */
+               /* VALID_AUDIO_IO_MUTE_LEVEL not yet handled
+                * (capture pipe level) */
                rmh.cmd[2] = chip->digital_capture_volume[channel];
        } else {
-               rmh.cmd[0] |= VALID_AUDIO_IO_MONITOR_LEVEL | VALID_AUDIO_IO_MUTE_MONITOR_1;
-               /* VALID_AUDIO_IO_DIGITAL_LEVEL and VALID_AUDIO_IO_MUTE_LEVEL not yet
-                * handled (playback pipe level)
+               rmh.cmd[0] |=   VALID_AUDIO_IO_MONITOR_LEVEL |
+                               VALID_AUDIO_IO_MUTE_MONITOR_1;
+               /* VALID_AUDIO_IO_DIGITAL_LEVEL and VALID_AUDIO_IO_MUTE_LEVEL
+                * not yet handled (playback pipe level)
                 */
                rmh.cmd[2] = chip->monitoring_volume[channel] << 10;
                if (chip->monitoring_active[channel] == 0)
@@ -284,8 +334,8 @@ static int pcxhr_update_audio_pipe_level(struct snd_pcxhr* chip, int capture, in
        rmh.cmd_len = 3;
 
        err = pcxhr_send_msg(chip->mgr, &rmh);
-       if(err<0) {
-               snd_printk(KERN_DEBUG "error update_audio_level card(%d) err(%x)\n",
+       if (err < 0) {
+               snd_printk(KERN_DEBUG "error update_audio_level(%d) err=%x\n",
                           chip->chip_idx, err);
                return -EINVAL;
        }
@@ -309,15 +359,15 @@ static int pcxhr_pcm_vol_get(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
-       int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);         /* index */
+       int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
        int *stored_volume;
        int is_capture = kcontrol->private_value;
 
        mutex_lock(&chip->mgr->mixer_mutex);
-       if (is_capture)
-               stored_volume = chip->digital_capture_volume;           /* digital capture */
-       else
-               stored_volume = chip->digital_playback_volume[idx];     /* digital playback */
+       if (is_capture)         /* digital capture */
+               stored_volume = chip->digital_capture_volume;
+       else                    /* digital playback */
+               stored_volume = chip->digital_playback_volume[idx];
        ucontrol->value.integer.value[0] = stored_volume[0];
        ucontrol->value.integer.value[1] = stored_volume[1];
        mutex_unlock(&chip->mgr->mixer_mutex);
@@ -328,7 +378,7 @@ static int pcxhr_pcm_vol_put(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
-       int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);         /* index */
+       int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
        int changed = 0;
        int is_capture = kcontrol->private_value;
        int *stored_volume;
@@ -384,7 +434,8 @@ static int pcxhr_pcm_sw_get(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
-static int pcxhr_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int pcxhr_pcm_sw_put(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
        int changed = 0;
@@ -444,8 +495,8 @@ static int pcxhr_monitor_vol_put(struct snd_kcontrol *kcontrol,
                if (chip->monitoring_volume[i] !=
                    ucontrol->value.integer.value[i]) {
                        chip->monitoring_volume[i] =
-                               !!ucontrol->value.integer.value[i];
-                       if(chip->monitoring_active[i])
+                               ucontrol->value.integer.value[i];
+                       if (chip->monitoring_active[i])
                                /* update monitoring volume and mute */
                                /* do only when monitoring is unmuted */
                                pcxhr_update_audio_pipe_level(chip, 0, i);
@@ -460,7 +511,7 @@ static struct snd_kcontrol_new pcxhr_control_monitor_vol = {
        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
        .access =       (SNDRV_CTL_ELEM_ACCESS_READWRITE |
                         SNDRV_CTL_ELEM_ACCESS_TLV_READ),
-       .name =         "Monitoring Volume",
+       .name =         "Monitoring Playback Volume",
        .info =         pcxhr_digital_vol_info,         /* shared */
        .get =          pcxhr_monitor_vol_get,
        .put =          pcxhr_monitor_vol_put,
@@ -511,7 +562,7 @@ static int pcxhr_monitor_sw_put(struct snd_kcontrol *kcontrol,
 
 static struct snd_kcontrol_new pcxhr_control_monitor_sw = {
        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
-       .name =         "Monitoring Switch",
+       .name =         "Monitoring Playback Switch",
        .info =         pcxhr_sw_info,          /* shared */
        .get =          pcxhr_monitor_sw_get,
        .put =          pcxhr_monitor_sw_put
@@ -533,7 +584,7 @@ static int pcxhr_set_audio_source(struct snd_pcxhr* chip)
        struct pcxhr_rmh rmh;
        unsigned int mask, reg;
        unsigned int codec;
-       int err, use_src, changed;
+       int err, changed;
 
        switch (chip->chip_idx) {
        case 0 : mask = PCXHR_SOURCE_AUDIO01_UER; codec = CS8420_01_CS; break;
@@ -542,13 +593,10 @@ static int pcxhr_set_audio_source(struct snd_pcxhr* chip)
        case 3 : mask = PCXHR_SOURCE_AUDIO67_UER; codec = CS8420_67_CS; break;
        default: return -EINVAL;
        }
-       reg = 0;        /* audio source from analog plug */
-       use_src = 0;    /* do not activate codec SRC */
-
        if (chip->audio_capture_source != 0) {
                reg = mask;     /* audio source from digital plug */
-               if (chip->audio_capture_source == 2)
-                       use_src = 1;
+       } else {
+               reg = 0;        /* audio source from analog plug */
        }
        /* set the input source */
        pcxhr_write_io_num_reg_cont(chip->mgr, mask, reg, &changed);
@@ -560,29 +608,61 @@ static int pcxhr_set_audio_source(struct snd_pcxhr* chip)
                if (err)
                        return err;
        }
-       pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);      /* set codec SRC on off */
-       rmh.cmd_len = 3;
-       rmh.cmd[0] |= IO_NUM_UER_CHIP_REG;
-       rmh.cmd[1] = codec;
-       rmh.cmd[2] = (CS8420_DATA_FLOW_CTL & CHIP_SIG_AND_MAP_SPI) | (use_src ? 0x41 : 0x54);
-       err = pcxhr_send_msg(chip->mgr, &rmh);
-       if(err)
-               return err;
-       rmh.cmd[2] = (CS8420_CLOCK_SRC_CTL & CHIP_SIG_AND_MAP_SPI) | (use_src ? 0x41 : 0x49);
-       err = pcxhr_send_msg(chip->mgr, &rmh);
+       if (chip->mgr->board_aes_in_192k) {
+               int i;
+               unsigned int src_config = 0xC0;
+               /* update all src configs with one call */
+               for (i = 0; (i < 4) && (i < chip->mgr->capture_chips); i++) {
+                       if (chip->mgr->chip[i]->audio_capture_source == 2)
+                               src_config |= (1 << (3 - i));
+               }
+               /* set codec SRC on off */
+               pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
+               rmh.cmd_len = 2;
+               rmh.cmd[0] |= IO_NUM_REG_CONFIG_SRC;
+               rmh.cmd[1] = src_config;
+               err = pcxhr_send_msg(chip->mgr, &rmh);
+       } else {
+               int use_src = 0;
+               if (chip->audio_capture_source == 2)
+                       use_src = 1;
+               /* set codec SRC on off */
+               pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
+               rmh.cmd_len = 3;
+               rmh.cmd[0] |= IO_NUM_UER_CHIP_REG;
+               rmh.cmd[1] = codec;
+               rmh.cmd[2] = ((CS8420_DATA_FLOW_CTL & CHIP_SIG_AND_MAP_SPI) |
+                             (use_src ? 0x41 : 0x54));
+               err = pcxhr_send_msg(chip->mgr, &rmh);
+               if (err)
+                       return err;
+               rmh.cmd[2] = ((CS8420_CLOCK_SRC_CTL & CHIP_SIG_AND_MAP_SPI) |
+                             (use_src ? 0x41 : 0x49));
+               err = pcxhr_send_msg(chip->mgr, &rmh);
+       }
        return err;
 }
 
 static int pcxhr_audio_src_info(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[3] = {"Analog", "Digital", "Digi+SRC"};
+       static const char *texts[5] = {
+               "Line", "Digital", "Digi+SRC", "Mic", "Line+Mic"
+       };
+       int i;
+       struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
 
+       i = 2;                  /* no SRC, no Mic available */
+       if (chip->mgr->board_has_aes1) {
+               i = 3;          /* SRC available */
+               if (chip->mgr->board_has_mic)
+                       i = 5;  /* Mic and MicroMix available */
+       }
        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
        uinfo->count = 1;
-       uinfo->value.enumerated.items = 3;
-       if (uinfo->value.enumerated.item > 2)
-               uinfo->value.enumerated.item = 2;
+       uinfo->value.enumerated.items = i;
+       if (uinfo->value.enumerated.item > (i-1))
+               uinfo->value.enumerated.item = i-1;
        strcpy(uinfo->value.enumerated.name,
                texts[uinfo->value.enumerated.item]);
        return 0;
@@ -601,13 +681,21 @@ static int pcxhr_audio_src_put(struct snd_kcontrol *kcontrol,
 {
        struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
        int ret = 0;
-
-       if (ucontrol->value.enumerated.item[0] >= 3)
+       int i = 2;              /* no SRC, no Mic available */
+       if (chip->mgr->board_has_aes1) {
+               i = 3;          /* SRC available */
+               if (chip->mgr->board_has_mic)
+                       i = 5;  /* Mic and MicroMix available */
+       }
+       if (ucontrol->value.enumerated.item[0] >= i)
                return -EINVAL;
        mutex_lock(&chip->mgr->mixer_mutex);
        if (chip->audio_capture_source != ucontrol->value.enumerated.item[0]) {
                chip->audio_capture_source = ucontrol->value.enumerated.item[0];
-               pcxhr_set_audio_source(chip);
+               if (chip->mgr->is_hr_stereo)
+                       hr222_set_audio_source(chip);
+               else
+                       pcxhr_set_audio_source(chip);
                ret = 1;
        }
        mutex_unlock(&chip->mgr->mixer_mutex);
@@ -626,25 +714,46 @@ static struct snd_kcontrol_new pcxhr_control_audio_src = {
 /*
  * clock type selection
  * enum pcxhr_clock_type {
- *             PCXHR_CLOCK_TYPE_INTERNAL = 0,
- *             PCXHR_CLOCK_TYPE_WORD_CLOCK,
- *             PCXHR_CLOCK_TYPE_AES_SYNC,
- *             PCXHR_CLOCK_TYPE_AES_1,
- *             PCXHR_CLOCK_TYPE_AES_2,
- *             PCXHR_CLOCK_TYPE_AES_3,
- *             PCXHR_CLOCK_TYPE_AES_4,
- *     };
+ *     PCXHR_CLOCK_TYPE_INTERNAL = 0,
+ *     PCXHR_CLOCK_TYPE_WORD_CLOCK,
+ *     PCXHR_CLOCK_TYPE_AES_SYNC,
+ *     PCXHR_CLOCK_TYPE_AES_1,
+ *     PCXHR_CLOCK_TYPE_AES_2,
+ *     PCXHR_CLOCK_TYPE_AES_3,
+ *     PCXHR_CLOCK_TYPE_AES_4,
+ *     PCXHR_CLOCK_TYPE_MAX = PCXHR_CLOCK_TYPE_AES_4,
+ *     HR22_CLOCK_TYPE_INTERNAL = PCXHR_CLOCK_TYPE_INTERNAL,
+ *     HR22_CLOCK_TYPE_AES_SYNC,
+ *     HR22_CLOCK_TYPE_AES_1,
+ *     HR22_CLOCK_TYPE_MAX = HR22_CLOCK_TYPE_AES_1,
+ * };
  */
 
 static int pcxhr_clock_type_info(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[7] = {
-               "Internal", "WordClock", "AES Sync", "AES 1", "AES 2", "AES 3", "AES 4"
+       static const char *textsPCXHR[7] = {
+               "Internal", "WordClock", "AES Sync",
+               "AES 1", "AES 2", "AES 3", "AES 4"
+       };
+       static const char *textsHR22[3] = {
+               "Internal", "AES Sync", "AES 1"
        };
+       const char **texts;
        struct pcxhr_mgr *mgr = snd_kcontrol_chip(kcontrol);
-       int clock_items = 3 + mgr->capture_chips;
-
+       int clock_items = 2;    /* at least Internal and AES Sync clock */
+       if (mgr->board_has_aes1) {
+               clock_items += mgr->capture_chips;      /* add AES x */
+               if (!mgr->is_hr_stereo)
+                       clock_items += 1;               /* add word clock */
+       }
+       if (mgr->is_hr_stereo) {
+               texts = textsHR22;
+               snd_BUG_ON(clock_items > (HR22_CLOCK_TYPE_MAX+1));
+       } else {
+               texts = textsPCXHR;
+               snd_BUG_ON(clock_items > (PCXHR_CLOCK_TYPE_MAX+1));
+       }
        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
        uinfo->count = 1;
        uinfo->value.enumerated.items = clock_items;
@@ -667,9 +776,13 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
        struct pcxhr_mgr *mgr = snd_kcontrol_chip(kcontrol);
-       unsigned int clock_items = 3 + mgr->capture_chips;
        int rate, ret = 0;
-
+       unsigned int clock_items = 2; /* at least Internal and AES Sync clock */
+       if (mgr->board_has_aes1) {
+               clock_items += mgr->capture_chips;      /* add AES x */
+               if (!mgr->is_hr_stereo)
+                       clock_items += 1;               /* add word clock */
+       }
        if (ucontrol->value.enumerated.item[0] >= clock_items)
                return -EINVAL;
        mutex_lock(&mgr->mixer_mutex);
@@ -677,7 +790,8 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol,
                mutex_lock(&mgr->setup_mutex);
                mgr->use_clock_type = ucontrol->value.enumerated.item[0];
                if (mgr->use_clock_type)
-                       pcxhr_get_external_clock(mgr, mgr->use_clock_type, &rate);
+                       pcxhr_get_external_clock(mgr, mgr->use_clock_type,
+                                                &rate);
                else
                        rate = mgr->sample_rate;
                if (rate) {
@@ -686,7 +800,7 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol,
                                mgr->sample_rate = rate;
                }
                mutex_unlock(&mgr->setup_mutex);
-               ret = 1;        /* return 1 even if the set was not done. ok ? */
+               ret = 1; /* return 1 even if the set was not done. ok ? */
        }
        mutex_unlock(&mgr->mixer_mutex);
        return ret;
@@ -747,14 +861,16 @@ static struct snd_kcontrol_new pcxhr_control_clock_rate = {
 /*
  * IEC958 status bits
  */
-static int pcxhr_iec958_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int pcxhr_iec958_info(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_info *uinfo)
 {
        uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
        uinfo->count = 1;
        return 0;
 }
 
-static int pcxhr_iec958_capture_byte(struct snd_pcxhr *chip, int aes_idx, unsigned char* aes_bits)
+static int pcxhr_iec958_capture_byte(struct snd_pcxhr *chip,
+                                    int aes_idx, unsigned char *aes_bits)
 {
        int i, err;
        unsigned char temp;
@@ -763,39 +879,61 @@ static int pcxhr_iec958_capture_byte(struct snd_pcxhr *chip, int aes_idx, unsign
        pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);
        rmh.cmd[0] |= IO_NUM_UER_CHIP_REG;
        switch (chip->chip_idx) {
-       case 0: rmh.cmd[1] = CS8420_01_CS; break;       /* use CS8416_01_CS for AES SYNC plug */
+         /* instead of CS8420_01_CS use CS8416_01_CS for AES SYNC plug */
+       case 0: rmh.cmd[1] = CS8420_01_CS; break;
        case 1: rmh.cmd[1] = CS8420_23_CS; break;
        case 2: rmh.cmd[1] = CS8420_45_CS; break;
        case 3: rmh.cmd[1] = CS8420_67_CS; break;
        default: return -EINVAL;
        }
-       switch (aes_idx) {
-       case 0: rmh.cmd[2] = CS8420_CSB0; break;        /* use CS8416_CSBx for AES SYNC plug */
-       case 1: rmh.cmd[2] = CS8420_CSB1; break;
-       case 2: rmh.cmd[2] = CS8420_CSB2; break;
-       case 3: rmh.cmd[2] = CS8420_CSB3; break;
-       case 4: rmh.cmd[2] = CS8420_CSB4; break;
-       default: return -EINVAL;
+       if (chip->mgr->board_aes_in_192k) {
+               switch (aes_idx) {
+               case 0: rmh.cmd[2] = CS8416_CSB0; break;
+               case 1: rmh.cmd[2] = CS8416_CSB1; break;
+               case 2: rmh.cmd[2] = CS8416_CSB2; break;
+               case 3: rmh.cmd[2] = CS8416_CSB3; break;
+               case 4: rmh.cmd[2] = CS8416_CSB4; break;
+               default: return -EINVAL;
+               }
+       } else {
+               switch (aes_idx) {
+                 /* instead of CS8420_CSB0 use CS8416_CSBx for AES SYNC plug */
+               case 0: rmh.cmd[2] = CS8420_CSB0; break;
+               case 1: rmh.cmd[2] = CS8420_CSB1; break;
+               case 2: rmh.cmd[2] = CS8420_CSB2; break;
+               case 3: rmh.cmd[2] = CS8420_CSB3; break;
+               case 4: rmh.cmd[2] = CS8420_CSB4; break;
+               default: return -EINVAL;
+               }
        }
-       rmh.cmd[1] &= 0x0fffff;                 /* size and code the chip id for the fpga */
-       rmh.cmd[2] &= CHIP_SIG_AND_MAP_SPI;     /* chip signature + map for spi read */
+       /* size and code the chip id for the fpga */
+       rmh.cmd[1] &= 0x0fffff;
+       /* chip signature + map for spi read */
+       rmh.cmd[2] &= CHIP_SIG_AND_MAP_SPI;
        rmh.cmd_len = 3;
        err = pcxhr_send_msg(chip->mgr, &rmh);
        if (err)
                return err;
-       temp = 0;
-       for (i = 0; i < 8; i++) {
-               /* attention : reversed bit order (not with CS8416_01_CS) */
-               temp <<= 1;
-               if (rmh.stat[1] & (1 << i))
-                       temp |= 1;
+
+       if (chip->mgr->board_aes_in_192k) {
+               temp = (unsigned char)rmh.stat[1];
+       } else {
+               temp = 0;
+               /* reversed bit order (not with CS8416_01_CS) */
+               for (i = 0; i < 8; i++) {
+                       temp <<= 1;
+                       if (rmh.stat[1] & (1 << i))
+                               temp |= 1;
+               }
        }
-       snd_printdd("read iec958 AES %d byte %d = 0x%x\n", chip->chip_idx, aes_idx, temp);
+       snd_printdd("read iec958 AES %d byte %d = 0x%x\n",
+                   chip->chip_idx, aes_idx, temp);
        *aes_bits = temp;
        return 0;
 }
 
-static int pcxhr_iec958_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int pcxhr_iec958_get(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
        unsigned char aes_bits;
@@ -806,7 +944,12 @@ static int pcxhr_iec958_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
                if (kcontrol->private_value == 0)       /* playback */
                        aes_bits = chip->aes_bits[i];
                else {                          /* capture */
-                       err = pcxhr_iec958_capture_byte(chip, i, &aes_bits);
+                       if (chip->mgr->is_hr_stereo)
+                               err = hr222_iec958_capture_byte(chip, i,
+                                                               &aes_bits);
+                       else
+                               err = pcxhr_iec958_capture_byte(chip, i,
+                                                               &aes_bits);
                        if (err)
                                break;
                }
@@ -825,7 +968,8 @@ static int pcxhr_iec958_mask_get(struct snd_kcontrol *kcontrol,
         return 0;
 }
 
-static int pcxhr_iec958_update_byte(struct snd_pcxhr *chip, int aes_idx, unsigned char aes_bits)
+static int pcxhr_iec958_update_byte(struct snd_pcxhr *chip,
+                                   int aes_idx, unsigned char aes_bits)
 {
        int i, err, cmd;
        unsigned char new_bits = aes_bits;
@@ -834,12 +978,12 @@ static int pcxhr_iec958_update_byte(struct snd_pcxhr *chip, int aes_idx, unsigne
 
        for (i = 0; i < 8; i++) {
                if ((old_bits & 0x01) != (new_bits & 0x01)) {
-                       cmd = chip->chip_idx & 0x03;            /* chip index 0..3 */
-                       if(chip->chip_idx > 3)
+                       cmd = chip->chip_idx & 0x03;      /* chip index 0..3 */
+                       if (chip->chip_idx > 3)
                                /* new bit used if chip_idx>3 (PCX1222HR) */
                                cmd |= 1 << 22;
-                       cmd |= ((aes_idx << 3) + i) << 2;       /* add bit offset */
-                       cmd |= (new_bits & 0x01) << 23;         /* add bit value */
+                       cmd |= ((aes_idx << 3) + i) << 2; /* add bit offset */
+                       cmd |= (new_bits & 0x01) << 23;   /* add bit value */
                        pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
                        rmh.cmd[0] |= IO_NUM_REG_CUER;
                        rmh.cmd[1] = cmd;
@@ -867,7 +1011,12 @@ static int pcxhr_iec958_put(struct snd_kcontrol *kcontrol,
        mutex_lock(&chip->mgr->mixer_mutex);
        for (i = 0; i < 5; i++) {
                if (ucontrol->value.iec958.status[i] != chip->aes_bits[i]) {
-                       pcxhr_iec958_update_byte(chip, i, ucontrol->value.iec958.status[i]);
+                       if (chip->mgr->is_hr_stereo)
+                               hr222_iec958_update_byte(chip, i,
+                                       ucontrol->value.iec958.status[i]);
+                       else
+                               pcxhr_iec958_update_byte(chip, i,
+                                       ucontrol->value.iec958.status[i]);
                        changed = 1;
                }
        }
@@ -917,29 +1066,53 @@ static void pcxhr_init_audio_levels(struct snd_pcxhr *chip)
                        /* at boot time the digital volumes are unmuted 0dB */
                        for (j = 0; j < PCXHR_PLAYBACK_STREAMS; j++) {
                                chip->digital_playback_active[j][i] = 1;
-                               chip->digital_playback_volume[j][i] = PCXHR_DIGITAL_ZERO_LEVEL;
+                               chip->digital_playback_volume[j][i] =
+                                       PCXHR_DIGITAL_ZERO_LEVEL;
                        }
-                       /* after boot, only two bits are set on the uer interface */
-                       chip->aes_bits[0] = IEC958_AES0_PROFESSIONAL | IEC958_AES0_PRO_FS_48000;
-/* only for test purpose, remove later */
+                       /* after boot, only two bits are set on the uer
+                        * interface
+                        */
+                       chip->aes_bits[0] = (IEC958_AES0_PROFESSIONAL |
+                                            IEC958_AES0_PRO_FS_48000);
 #ifdef CONFIG_SND_DEBUG
-                       /* analog volumes for playback (is LEVEL_MIN after boot) */
+                       /* analog volumes for playback
+                        * (is LEVEL_MIN after boot)
+                        */
                        chip->analog_playback_active[i] = 1;
-                       chip->analog_playback_volume[i] = PCXHR_ANALOG_PLAYBACK_ZERO_LEVEL;
-                       pcxhr_update_analog_audio_level(chip, 0, i);
+                       if (chip->mgr->is_hr_stereo)
+                               chip->analog_playback_volume[i] =
+                                       HR222_LINE_PLAYBACK_ZERO_LEVEL;
+                       else {
+                               chip->analog_playback_volume[i] =
+                                       PCXHR_LINE_PLAYBACK_ZERO_LEVEL;
+                               pcxhr_update_analog_audio_level(chip, 0, i);
+                       }
 #endif
-/* test end */
+                       /* stereo cards need to be initialised after boot */
+                       if (chip->mgr->is_hr_stereo)
+                               hr222_update_analog_audio_level(chip, 0, i);
                }
                if (chip->nb_streams_capt) {
                        /* at boot time the digital volumes are unmuted 0dB */
-                       chip->digital_capture_volume[i] = PCXHR_DIGITAL_ZERO_LEVEL;
-/* only for test purpose, remove later */
+                       chip->digital_capture_volume[i] =
+                               PCXHR_DIGITAL_ZERO_LEVEL;
+                       chip->analog_capture_active = 1;
 #ifdef CONFIG_SND_DEBUG
-                       /* analog volumes for playback (is LEVEL_MIN after boot) */
-                       chip->analog_capture_volume[i]  = PCXHR_ANALOG_CAPTURE_ZERO_LEVEL;
-                       pcxhr_update_analog_audio_level(chip, 1, i);
+                       /* analog volumes for playback
+                        * (is LEVEL_MIN after boot)
+                        */
+                       if (chip->mgr->is_hr_stereo)
+                               chip->analog_capture_volume[i] =
+                                       HR222_LINE_CAPTURE_ZERO_LEVEL;
+                       else {
+                               chip->analog_capture_volume[i] =
+                                       PCXHR_LINE_CAPTURE_ZERO_LEVEL;
+                               pcxhr_update_analog_audio_level(chip, 1, i);
+                       }
 #endif
-/* test end */
+                       /* stereo cards need to be initialised after boot */
+                       if (chip->mgr->is_hr_stereo)
+                               hr222_update_analog_audio_level(chip, 1, i);
                }
        }
 
@@ -963,90 +1136,125 @@ int pcxhr_create_mixer(struct pcxhr_mgr *mgr)
                        temp = pcxhr_control_analog_level;
                        temp.name = "Master Playback Volume";
                        temp.private_value = 0; /* playback */
-                       temp.tlv.p = db_scale_analog_playback;
-                       if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+                       if (mgr->is_hr_stereo)
+                               temp.tlv.p = db_scale_a_hr222_playback;
+                       else
+                               temp.tlv.p = db_scale_analog_playback;
+                       err = snd_ctl_add(chip->card,
+                                         snd_ctl_new1(&temp, chip));
+                       if (err < 0)
                                return err;
+
                        /* output mute controls */
-                       if ((err = snd_ctl_add(chip->card,
-                                              snd_ctl_new1(&pcxhr_control_output_switch,
-                                                           chip))) < 0)
+                       err = snd_ctl_add(chip->card,
+                               snd_ctl_new1(&pcxhr_control_output_switch,
+                                            chip));
+                       if (err < 0)
                                return err;
-                       
+
                        temp = snd_pcxhr_pcm_vol;
                        temp.name = "PCM Playback Volume";
                        temp.count = PCXHR_PLAYBACK_STREAMS;
                        temp.private_value = 0; /* playback */
-                       if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+                       err = snd_ctl_add(chip->card,
+                                         snd_ctl_new1(&temp, chip));
+                       if (err < 0)
                                return err;
 
-                       if ((err = snd_ctl_add(chip->card,
-                                              snd_ctl_new1(&pcxhr_control_pcm_switch,
-                                                           chip))) < 0)
+                       err = snd_ctl_add(chip->card,
+                               snd_ctl_new1(&pcxhr_control_pcm_switch, chip));
+                       if (err < 0)
                                return err;
 
                        /* IEC958 controls */
-                       if ((err = snd_ctl_add(chip->card,
-                                              snd_ctl_new1(&pcxhr_control_playback_iec958_mask,
-                                                           chip))) < 0)
+                       err = snd_ctl_add(chip->card,
+                               snd_ctl_new1(&pcxhr_control_playback_iec958_mask,
+                                            chip));
+                       if (err < 0)
                                return err;
-                       if ((err = snd_ctl_add(chip->card,
-                                              snd_ctl_new1(&pcxhr_control_playback_iec958,
-                                                           chip))) < 0)
+
+                       err = snd_ctl_add(chip->card,
+                               snd_ctl_new1(&pcxhr_control_playback_iec958,
+                                            chip));
+                       if (err < 0)
                                return err;
                }
                if (chip->nb_streams_capt) {
-                       /* analog input level control only on first two chips !*/
+                       /* analog input level control */
                        temp = pcxhr_control_analog_level;
-                       temp.name = "Master Capture Volume";
+                       temp.name = "Line Capture Volume";
                        temp.private_value = 1; /* capture */
-                       temp.tlv.p = db_scale_analog_capture;
-                       if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+                       if (mgr->is_hr_stereo)
+                               temp.tlv.p = db_scale_a_hr222_capture;
+                       else
+                               temp.tlv.p = db_scale_analog_capture;
+
+                       err = snd_ctl_add(chip->card,
+                                         snd_ctl_new1(&temp, chip));
+                       if (err < 0)
                                return err;
 
                        temp = snd_pcxhr_pcm_vol;
                        temp.name = "PCM Capture Volume";
                        temp.count = 1;
                        temp.private_value = 1; /* capture */
-                       if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+
+                       err = snd_ctl_add(chip->card,
+                                         snd_ctl_new1(&temp, chip));
+                       if (err < 0)
                                return err;
+
                        /* Audio source */
-                       if ((err = snd_ctl_add(chip->card,
-                                              snd_ctl_new1(&pcxhr_control_audio_src,
-                                                           chip))) < 0)
+                       err = snd_ctl_add(chip->card,
+                               snd_ctl_new1(&pcxhr_control_audio_src, chip));
+                       if (err < 0)
                                return err;
+
                        /* IEC958 controls */
-                       if ((err = snd_ctl_add(chip->card,
-                                              snd_ctl_new1(&pcxhr_control_capture_iec958_mask,
-                                                           chip))) < 0)
+                       err = snd_ctl_add(chip->card,
+                               snd_ctl_new1(&pcxhr_control_capture_iec958_mask,
+                                            chip));
+                       if (err < 0)
                                return err;
-                       if ((err = snd_ctl_add(chip->card,
-                                              snd_ctl_new1(&pcxhr_control_capture_iec958,
-                                                           chip))) < 0)
+
+                       err = snd_ctl_add(chip->card,
+                               snd_ctl_new1(&pcxhr_control_capture_iec958,
+                                            chip));
+                       if (err < 0)
                                return err;
+
+                       if (mgr->is_hr_stereo) {
+                               err = hr222_add_mic_controls(chip);
+                               if (err < 0)
+                                       return err;
+                       }
                }
                /* monitoring only if playback and capture device available */
                if (chip->nb_streams_capt > 0 && chip->nb_streams_play > 0) {
                        /* monitoring */
-                       if ((err = snd_ctl_add(chip->card,
-                                              snd_ctl_new1(&pcxhr_control_monitor_vol,
-                                                           chip))) < 0)
+                       err = snd_ctl_add(chip->card,
+                               snd_ctl_new1(&pcxhr_control_monitor_vol, chip));
+                       if (err < 0)
                                return err;
-                       if ((err = snd_ctl_add(chip->card,
-                                              snd_ctl_new1(&pcxhr_control_monitor_sw,
-                                                           chip))) < 0)
+
+                       err = snd_ctl_add(chip->card,
+                               snd_ctl_new1(&pcxhr_control_monitor_sw, chip));
+                       if (err < 0)
                                return err;
                }
 
                if (i == 0) {
                        /* clock mode only one control per pcxhr */
-                       if ((err = snd_ctl_add(chip->card,
-                                              snd_ctl_new1(&pcxhr_control_clock_type,
-                                                           mgr))) < 0)
+                       err = snd_ctl_add(chip->card,
+                               snd_ctl_new1(&pcxhr_control_clock_type, mgr));
+                       if (err < 0)
                                return err;
-                       /* non standard control used to scan the external clock presence/frequencies */
-                       if ((err = snd_ctl_add(chip->card,
-                                              snd_ctl_new1(&pcxhr_control_clock_rate,
-                                                           mgr))) < 0)
+                       /* non standard control used to scan
+                        * the external clock presence/frequencies
+                        */
+                       err = snd_ctl_add(chip->card,
+                               snd_ctl_new1(&pcxhr_control_clock_rate, mgr));
+                       if (err < 0)
                                return err;
                }
 
index e9f0706..3caacfb 100644 (file)
@@ -172,7 +172,7 @@ MODULE_PARM_DESC(opl3_port, "OPL3 port # for Riptide driver.");
 
 #define MAX_WRITE_RETRY  10    /* cmd interface limits */
 #define MAX_ERROR_COUNT  10
-#define CMDIF_TIMEOUT    500000
+#define CMDIF_TIMEOUT    50000
 #define RESET_TRIES      5
 
 #define READ_PORT_ULONG(p)     inl((unsigned long)&(p))
@@ -1754,7 +1754,7 @@ snd_riptide_interrupt(int irq, void *dev_id)
                if (IS_EOBIRQ(cif->hwport) || IS_EOSIRQ(cif->hwport) ||
                    IS_EOCIRQ(cif->hwport)) {
                        chip->handled_irqs++;
-                       tasklet_hi_schedule(&chip->riptide_tq);
+                       tasklet_schedule(&chip->riptide_tq);
                }
                if (chip->rmidi && IS_MPUIRQ(cif->hwport)) {
                        chip->handled_irqs++;
index 736246f..44d0c15 100644 (file)
@@ -1452,7 +1452,7 @@ static int snd_hdsp_create_midi (struct snd_card *card, struct hdsp *hdsp, int i
        if (snd_rawmidi_new (card, buf, id, 1, 1, &hdsp->midi[id].rmidi) < 0)
                return -1;
 
-       sprintf (hdsp->midi[id].rmidi->name, "%s MIDI %d", card->id, id+1);
+       sprintf(hdsp->midi[id].rmidi->name, "HDSP MIDI %d", id+1);
        hdsp->midi[id].rmidi->private_data = &hdsp->midi[id];
 
        snd_rawmidi_set_ops (hdsp->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_hdsp_midi_output);
@@ -3750,7 +3750,7 @@ static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id)
                }
        }
        if (hdsp->use_midi_tasklet && schedule)
-               tasklet_hi_schedule(&hdsp->midi_tasklet);
+               tasklet_schedule(&hdsp->midi_tasklet);
        return IRQ_HANDLED;
 }
 
index 98762f9..71231cf 100644 (file)
@@ -1293,7 +1293,7 @@ static int __devinit snd_hdspm_create_midi (struct snd_card *card,
        if (err < 0)
                return err;
 
-       sprintf (hdspm->midi[id].rmidi->name, "%s MIDI %d", card->id, id+1);
+       sprintf(hdspm->midi[id].rmidi->name, "HDSPM MIDI %d", id+1);
        hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
 
        snd_rawmidi_set_ops(hdspm->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
@@ -3476,7 +3476,7 @@ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id)
                schedule = 1;
        }
        if (schedule)
-               tasklet_hi_schedule(&hdspm->midi_tasklet);
+               tasklet_schedule(&hdspm->midi_tasklet);
        return IRQ_HANDLED;
 }
 
index fa4b113..ea903c8 100644 (file)
@@ -41,7 +41,7 @@ irqreturn_t pdacf_interrupt(int irq, void *dev)
                if (stat & PDAUDIOCF_IRQOVR)    /* should never happen */
                        snd_printk(KERN_ERR "PDAUDIOCF SRAM buffer overrun detected!\n");
                if (chip->pcm_substream)
-                       tasklet_hi_schedule(&chip->tq);
+                       tasklet_schedule(&chip->tq);
                if (!(stat & PDAUDIOCF_IRQAKM))
                        stat |= PDAUDIOCF_IRQAKM;       /* check rate */
        }
index a38c0c7..af76ee8 100644 (file)
@@ -1033,7 +1033,7 @@ static int __init snd_pmac_detect(struct snd_pmac *chip)
        }
        if (of_device_is_compatible(sound, "tumbler")) {
                chip->model = PMAC_TUMBLER;
-               chip->can_capture = 0;  /* no capture */
+               chip->can_capture = machine_is_compatible("PowerMac4,2");
                chip->can_duplex = 0;
                // chip->can_byte_swap = 0; /* FIXME: check this */
                chip->num_freqs = ARRAY_SIZE(tumbler_freqs);
index f746e15..3eb2233 100644 (file)
@@ -875,7 +875,8 @@ static struct snd_kcontrol_new snapper_mixers[] __initdata = {
          .put = tumbler_put_master_switch
        },
        DEFINE_SNAPPER_MIX("PCM Playback Volume", 0, VOL_IDX_PCM),
-       DEFINE_SNAPPER_MIX("PCM Playback Volume", 1, VOL_IDX_PCM2),
+       /* Alternative PCM is assigned to Mic analog loopback on iBook G4 */
+       DEFINE_SNAPPER_MIX("Mic Playback Volume", 0, VOL_IDX_PCM2),
        DEFINE_SNAPPER_MIX("Monitor Mix Volume", 0, VOL_IDX_ADC),
        DEFINE_SNAPPER_MONO("Tone Control - Bass", bass),
        DEFINE_SNAPPER_MONO("Tone Control - Treble", treble),
index 4dfda66..ef025c6 100644 (file)
@@ -22,17 +22,16 @@ if SND_SOC
 config SND_SOC_AC97_BUS
        bool
 
-# All the supported Soc's
-source "sound/soc/at32/Kconfig"
-source "sound/soc/at91/Kconfig"
+# All the supported SoCs
+source "sound/soc/atmel/Kconfig"
 source "sound/soc/au1x/Kconfig"
+source "sound/soc/blackfin/Kconfig"
+source "sound/soc/davinci/Kconfig"
+source "sound/soc/fsl/Kconfig"
+source "sound/soc/omap/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"
 source "sound/soc/sh/Kconfig"
-source "sound/soc/fsl/Kconfig"
-source "sound/soc/davinci/Kconfig"
-source "sound/soc/omap/Kconfig"
-source "sound/soc/blackfin/Kconfig"
 
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
index d849349..86a9b1f 100644 (file)
@@ -1,5 +1,13 @@
 snd-soc-core-objs := soc-core.o soc-dapm.o
 
 obj-$(CONFIG_SND_SOC)  += snd-soc-core.o
-obj-$(CONFIG_SND_SOC)  += codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/
-obj-$(CONFIG_SND_SOC)  += omap/ au1x/ blackfin/
+obj-$(CONFIG_SND_SOC)  += codecs/
+obj-$(CONFIG_SND_SOC)  += atmel/
+obj-$(CONFIG_SND_SOC)  += au1x/
+obj-$(CONFIG_SND_SOC)  += blackfin/
+obj-$(CONFIG_SND_SOC)  += davinci/
+obj-$(CONFIG_SND_SOC)  += fsl/
+obj-$(CONFIG_SND_SOC)  += omap/
+obj-$(CONFIG_SND_SOC)  += pxa/
+obj-$(CONFIG_SND_SOC)  += s3c24xx/
+obj-$(CONFIG_SND_SOC)  += sh/
diff --git a/sound/soc/at32/Kconfig b/sound/soc/at32/Kconfig
deleted file mode 100644 (file)
index b0765e8..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-config SND_AT32_SOC
-        tristate "SoC Audio for the Atmel AT32 System-on-a-Chip"
-        depends on AVR32 && SND_SOC
-        help
-          Say Y or M if you want to add support for codecs attached to 
-          the AT32 SSC interface.  You will also need to
-          to select the audio interfaces to support below.
-
-
-config SND_AT32_SOC_SSC
-        tristate
-
-
-
-config SND_AT32_SOC_PLAYPAQ
-        tristate "SoC Audio support for PlayPaq with WM8510"
-        depends on SND_AT32_SOC && BOARD_PLAYPAQ
-        select SND_AT32_SOC_SSC
-        select SND_SOC_WM8510
-        help
-          Say Y or M here if you want to add support for SoC audio
-          on the LRS PlayPaq.
-
-
-
-config SND_AT32_SOC_PLAYPAQ_SLAVE
-        bool "Run CODEC on PlayPaq in slave mode"
-        depends on SND_AT32_SOC_PLAYPAQ
-        default n
-        help
-          Say Y if you want to run with the AT32 SSC generating the BCLK
-          and FRAME signals on the PlayPaq.  Unless you want to play
-          with the AT32 as the SSC master, you probably want to say N here,
-          as this will give you better sound quality.
diff --git a/sound/soc/at32/Makefile b/sound/soc/at32/Makefile
deleted file mode 100644 (file)
index c03e55e..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# AT32 Platform Support
-snd-soc-at32-objs := at32-pcm.o
-snd-soc-at32-ssc-objs := at32-ssc.o
-
-obj-$(CONFIG_SND_AT32_SOC) += snd-soc-at32.o
-obj-$(CONFIG_SND_AT32_SOC_SSC) += snd-soc-at32-ssc.o
-
-# AT32 Machine Support
-snd-soc-playpaq-objs := playpaq_wm8510.o
-
-obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o
diff --git a/sound/soc/at32/at32-pcm.c b/sound/soc/at32/at32-pcm.c
deleted file mode 100644 (file)
index c83584f..0000000
+++ /dev/null
@@ -1,492 +0,0 @@
-/* sound/soc/at32/at32-pcm.c
- * ASoC PCM interface for Atmel AT32 SoC
- *
- * Copyright (C) 2008 Long Range Systems
- *    Geoffrey Wossum <gwossum@acm.org>
- *
- * 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.
- *
- * Note that this is basically a port of the sound/soc/at91-pcm.c to
- * the AVR32 kernel.  Thanks to Frank Mandarino for that code.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-#include <linux/atmel_pdc.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include "at32-pcm.h"
-
-
-
-/*--------------------------------------------------------------------------*\
- * Hardware definition
-\*--------------------------------------------------------------------------*/
-/* TODO: These values were taken from the AT91 platform driver, check
- *      them against real values for AT32
- */
-static const struct snd_pcm_hardware at32_pcm_hardware = {
-       .info = (SNDRV_PCM_INFO_MMAP |
-                SNDRV_PCM_INFO_MMAP_VALID |
-                SNDRV_PCM_INFO_INTERLEAVED |
-                SNDRV_PCM_INFO_BLOCK_TRANSFER |
-                SNDRV_PCM_INFO_PAUSE),
-
-       .formats = SNDRV_PCM_FMTBIT_S16,
-       .period_bytes_min = 32,
-       .period_bytes_max = 8192,       /* 512 frames * 16 bytes / frame */
-       .periods_min = 2,
-       .periods_max = 1024,
-       .buffer_bytes_max = 32 * 1024,
-};
-
-
-
-/*--------------------------------------------------------------------------*\
- * Data types
-\*--------------------------------------------------------------------------*/
-struct at32_runtime_data {
-       struct at32_pcm_dma_params *params;
-       dma_addr_t dma_buffer;  /* physical address of DMA buffer */
-       dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
-       size_t period_size;
-
-       dma_addr_t period_ptr;  /* physical address of next period */
-       int periods;            /* period index of period_ptr */
-
-       /* Save PDC registers (for power management) */
-       u32 pdc_xpr_save;
-       u32 pdc_xcr_save;
-       u32 pdc_xnpr_save;
-       u32 pdc_xncr_save;
-};
-
-
-
-/*--------------------------------------------------------------------------*\
- * Helper functions
-\*--------------------------------------------------------------------------*/
-static int at32_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
-       struct snd_pcm_substream *substream = pcm->streams[stream].substream;
-       struct snd_dma_buffer *dmabuf = &substream->dma_buffer;
-       size_t size = at32_pcm_hardware.buffer_bytes_max;
-
-       dmabuf->dev.type = SNDRV_DMA_TYPE_DEV;
-       dmabuf->dev.dev = pcm->card->dev;
-       dmabuf->private_data = NULL;
-       dmabuf->area = dma_alloc_coherent(pcm->card->dev, size,
-                                         &dmabuf->addr, GFP_KERNEL);
-       pr_debug("at32_pcm: preallocate_dma_buffer: "
-                "area=%p, addr=%p, size=%ld\n",
-                (void *)dmabuf->area, (void *)dmabuf->addr, size);
-
-       if (!dmabuf->area)
-               return -ENOMEM;
-
-       dmabuf->bytes = size;
-       return 0;
-}
-
-
-
-/*--------------------------------------------------------------------------*\
- * ISR
-\*--------------------------------------------------------------------------*/
-static void at32_pcm_dma_irq(u32 ssc_sr, struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *rtd = substream->runtime;
-       struct at32_runtime_data *prtd = rtd->private_data;
-       struct at32_pcm_dma_params *params = prtd->params;
-       static int count;
-
-       count++;
-       if (ssc_sr & params->mask->ssc_endbuf) {
-               pr_warning("at32-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
-                          substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
-                          "underrun" : "overrun", params->name, ssc_sr, count);
-
-               /* re-start the PDC */
-               ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-                          params->mask->pdc_disable);
-               prtd->period_ptr += prtd->period_size;
-               if (prtd->period_ptr >= prtd->dma_buffer_end)
-                       prtd->period_ptr = prtd->dma_buffer;
-
-
-               ssc_writex(params->ssc->regs, params->pdc->xpr,
-                          prtd->period_ptr);
-               ssc_writex(params->ssc->regs, params->pdc->xcr,
-                          prtd->period_size / params->pdc_xfer_size);
-               ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-                          params->mask->pdc_enable);
-       }
-
-
-       if (ssc_sr & params->mask->ssc_endx) {
-               /* Load the PDC next pointer and counter registers */
-               prtd->period_ptr += prtd->period_size;
-               if (prtd->period_ptr >= prtd->dma_buffer_end)
-                       prtd->period_ptr = prtd->dma_buffer;
-               ssc_writex(params->ssc->regs, params->pdc->xnpr,
-                          prtd->period_ptr);
-               ssc_writex(params->ssc->regs, params->pdc->xncr,
-                          prtd->period_size / params->pdc_xfer_size);
-       }
-
-
-       snd_pcm_period_elapsed(substream);
-}
-
-
-
-/*--------------------------------------------------------------------------*\
- * PCM operations
-\*--------------------------------------------------------------------------*/
-static int at32_pcm_hw_params(struct snd_pcm_substream *substream,
-                             struct snd_pcm_hw_params *params)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct at32_runtime_data *prtd = runtime->private_data;
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
-       /* this may get called several times by oss emulation
-        * with different params
-        */
-       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-       runtime->dma_bytes = params_buffer_bytes(params);
-
-       prtd->params = rtd->dai->cpu_dai->dma_data;
-       prtd->params->dma_intr_handler = at32_pcm_dma_irq;
-
-       prtd->dma_buffer = runtime->dma_addr;
-       prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
-       prtd->period_size = params_period_bytes(params);
-
-       pr_debug("hw_params: DMA for %s initialized "
-                "(dma_bytes=%ld, period_size=%ld)\n",
-                prtd->params->name, runtime->dma_bytes, prtd->period_size);
-
-       return 0;
-}
-
-
-
-static int at32_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-       struct at32_runtime_data *prtd = substream->runtime->private_data;
-       struct at32_pcm_dma_params *params = prtd->params;
-
-       if (params != NULL) {
-               ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
-                          params->mask->pdc_disable);
-               prtd->params->dma_intr_handler = NULL;
-       }
-
-       return 0;
-}
-
-
-
-static int at32_pcm_prepare(struct snd_pcm_substream *substream)
-{
-       struct at32_runtime_data *prtd = substream->runtime->private_data;
-       struct at32_pcm_dma_params *params = prtd->params;
-
-       ssc_writex(params->ssc->regs, SSC_IDR,
-                  params->mask->ssc_endx | params->mask->ssc_endbuf);
-       ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-                  params->mask->pdc_disable);
-
-       return 0;
-}
-
-
-static int at32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-       struct snd_pcm_runtime *rtd = substream->runtime;
-       struct at32_runtime_data *prtd = rtd->private_data;
-       struct at32_pcm_dma_params *params = prtd->params;
-       int ret = 0;
-
-       pr_debug("at32_pcm_trigger: buffer_size = %ld, "
-                "dma_area = %p, dma_bytes = %ld\n",
-                rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-               prtd->period_ptr = prtd->dma_buffer;
-
-               ssc_writex(params->ssc->regs, params->pdc->xpr,
-                          prtd->period_ptr);
-               ssc_writex(params->ssc->regs, params->pdc->xcr,
-                          prtd->period_size / params->pdc_xfer_size);
-
-               prtd->period_ptr += prtd->period_size;
-               ssc_writex(params->ssc->regs, params->pdc->xnpr,
-                          prtd->period_ptr);
-               ssc_writex(params->ssc->regs, params->pdc->xncr,
-                          prtd->period_size / params->pdc_xfer_size);
-
-               pr_debug("trigger: period_ptr=%lx, xpr=%x, "
-                        "xcr=%d, xnpr=%x, xncr=%d\n",
-                        (unsigned long)prtd->period_ptr,
-                        ssc_readx(params->ssc->regs, params->pdc->xpr),
-                        ssc_readx(params->ssc->regs, params->pdc->xcr),
-                        ssc_readx(params->ssc->regs, params->pdc->xnpr),
-                        ssc_readx(params->ssc->regs, params->pdc->xncr));
-
-               ssc_writex(params->ssc->regs, SSC_IER,
-                          params->mask->ssc_endx | params->mask->ssc_endbuf);
-               ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
-                          params->mask->pdc_enable);
-
-               pr_debug("sr=%x, imr=%x\n",
-                        ssc_readx(params->ssc->regs, SSC_SR),
-                        ssc_readx(params->ssc->regs, SSC_IER));
-               break;          /* SNDRV_PCM_TRIGGER_START */
-
-
-
-       case SNDRV_PCM_TRIGGER_STOP:
-       case SNDRV_PCM_TRIGGER_SUSPEND:
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-                          params->mask->pdc_disable);
-               break;
-
-
-       case SNDRV_PCM_TRIGGER_RESUME:
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-                          params->mask->pdc_enable);
-               break;
-
-       default:
-               ret = -EINVAL;
-       }
-
-       return ret;
-}
-
-
-
-static snd_pcm_uframes_t at32_pcm_pointer(struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct at32_runtime_data *prtd = runtime->private_data;
-       struct at32_pcm_dma_params *params = prtd->params;
-       dma_addr_t ptr;
-       snd_pcm_uframes_t x;
-
-       ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
-       x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
-
-       if (x == runtime->buffer_size)
-               x = 0;
-
-       return x;
-}
-
-
-
-static int at32_pcm_open(struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct at32_runtime_data *prtd;
-       int ret = 0;
-
-       snd_soc_set_runtime_hwparams(substream, &at32_pcm_hardware);
-
-       /* ensure that buffer size is a multiple of period size */
-       ret = snd_pcm_hw_constraint_integer(runtime,
-                                           SNDRV_PCM_HW_PARAM_PERIODS);
-       if (ret < 0)
-               goto out;
-
-       prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
-       if (prtd == NULL) {
-               ret = -ENOMEM;
-               goto out;
-       }
-       runtime->private_data = prtd;
-
-
-out:
-       return ret;
-}
-
-
-
-static int at32_pcm_close(struct snd_pcm_substream *substream)
-{
-       struct at32_runtime_data *prtd = substream->runtime->private_data;
-
-       kfree(prtd);
-       return 0;
-}
-
-
-static int at32_pcm_mmap(struct snd_pcm_substream *substream,
-                        struct vm_area_struct *vma)
-{
-       return remap_pfn_range(vma, vma->vm_start,
-                              substream->dma_buffer.addr >> PAGE_SHIFT,
-                              vma->vm_end - vma->vm_start, vma->vm_page_prot);
-}
-
-
-
-static struct snd_pcm_ops at32_pcm_ops = {
-       .open = at32_pcm_open,
-       .close = at32_pcm_close,
-       .ioctl = snd_pcm_lib_ioctl,
-       .hw_params = at32_pcm_hw_params,
-       .hw_free = at32_pcm_hw_free,
-       .prepare = at32_pcm_prepare,
-       .trigger = at32_pcm_trigger,
-       .pointer = at32_pcm_pointer,
-       .mmap = at32_pcm_mmap,
-};
-
-
-
-/*--------------------------------------------------------------------------*\
- * ASoC platform driver
-\*--------------------------------------------------------------------------*/
-static u64 at32_pcm_dmamask = 0xffffffff;
-
-static int at32_pcm_new(struct snd_card *card,
-                       struct snd_soc_dai *dai,
-                       struct snd_pcm *pcm)
-{
-       int ret = 0;
-
-       if (!card->dev->dma_mask)
-               card->dev->dma_mask = &at32_pcm_dmamask;
-       if (!card->dev->coherent_dma_mask)
-               card->dev->coherent_dma_mask = 0xffffffff;
-
-       if (dai->playback.channels_min) {
-               ret = at32_pcm_preallocate_dma_buffer(
-                         pcm, SNDRV_PCM_STREAM_PLAYBACK);
-               if (ret)
-                       goto out;
-       }
-
-       if (dai->capture.channels_min) {
-               pr_debug("at32-pcm: Allocating PCM capture DMA buffer\n");
-               ret = at32_pcm_preallocate_dma_buffer(
-                         pcm, SNDRV_PCM_STREAM_CAPTURE);
-               if (ret)
-                       goto out;
-       }
-
-
-out:
-       return ret;
-}
-
-
-
-static void at32_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
-       struct snd_pcm_substream *substream;
-       struct snd_dma_buffer *buf;
-       int stream;
-
-       for (stream = 0; stream < 2; stream++) {
-               substream = pcm->streams[stream].substream;
-               if (substream == NULL)
-                       continue;
-
-               buf = &substream->dma_buffer;
-               if (!buf->area)
-                       continue;
-               dma_free_coherent(pcm->card->dev, buf->bytes,
-                                 buf->area, buf->addr);
-               buf->area = NULL;
-       }
-}
-
-
-
-#ifdef CONFIG_PM
-static int at32_pcm_suspend(struct platform_device *pdev,
-                           struct snd_soc_dai *dai)
-{
-       struct snd_pcm_runtime *runtime = dai->runtime;
-       struct at32_runtime_data *prtd;
-       struct at32_pcm_dma_params *params;
-
-       if (runtime == NULL)
-               return 0;
-       prtd = runtime->private_data;
-       params = prtd->params;
-
-       /* Disable the PDC and save the PDC registers */
-       ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-                  params->mask->pdc_disable);
-
-       prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
-       prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
-       prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
-       prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
-
-       return 0;
-}
-
-
-
-static int at32_pcm_resume(struct platform_device *pdev,
-                          struct snd_soc_dai *dai)
-{
-       struct snd_pcm_runtime *runtime = dai->runtime;
-       struct at32_runtime_data *prtd;
-       struct at32_pcm_dma_params *params;
-
-       if (runtime == NULL)
-               return 0;
-       prtd = runtime->private_data;
-       params = prtd->params;
-
-       /* Restore the PDC registers and enable the PDC */
-       ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
-       ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
-       ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
-       ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
-
-       ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, params->mask->pdc_enable);
-       return 0;
-}
-#else /* CONFIG_PM */
-#  define at32_pcm_suspend     NULL
-#  define at32_pcm_resume      NULL
-#endif /* CONFIG_PM */
-
-
-
-struct snd_soc_platform at32_soc_platform = {
-       .name = "at32-audio",
-       .pcm_ops = &at32_pcm_ops,
-       .pcm_new = at32_pcm_new,
-       .pcm_free = at32_pcm_free_dma_buffers,
-       .suspend = at32_pcm_suspend,
-       .resume = at32_pcm_resume,
-};
-EXPORT_SYMBOL_GPL(at32_soc_platform);
-
-
-
-MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
-MODULE_DESCRIPTION("Atmel AT32 PCM module");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/at32/at32-pcm.h b/sound/soc/at32/at32-pcm.h
deleted file mode 100644 (file)
index 2a52430..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/* sound/soc/at32/at32-pcm.h
- * ASoC PCM interface for Atmel AT32 SoC
- *
- * Copyright (C) 2008 Long Range Systems
- *    Geoffrey Wossum <gwossum@acm.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef __SOUND_SOC_AT32_AT32_PCM_H
-#define __SOUND_SOC_AT32_AT32_PCM_H __FILE__
-
-#include <linux/atmel-ssc.h>
-
-
-/*
- * Registers and status bits that are required by the PCM driver
- * TODO: Is ptcr really used?
- */
-struct at32_pdc_regs {
-       u32 xpr;                /* PDC RX/TX pointer */
-       u32 xcr;                /* PDC RX/TX counter */
-       u32 xnpr;               /* PDC next RX/TX pointer */
-       u32 xncr;               /* PDC next RX/TX counter */
-       u32 ptcr;               /* PDC transfer control */
-};
-
-
-
-/*
- * SSC mask info
- */
-struct at32_ssc_mask {
-       u32 ssc_enable;         /* SSC RX/TX enable */
-       u32 ssc_disable;        /* SSC RX/TX disable */
-       u32 ssc_endx;           /* SSC ENDTX or ENDRX */
-       u32 ssc_endbuf;         /* SSC TXBUFF or RXBUFF */
-       u32 pdc_enable;         /* PDC RX/TX enable */
-       u32 pdc_disable;        /* PDC RX/TX disable */
-};
-
-
-
-/*
- * This structure, shared between the PCM driver and the interface,
- * contains all information required by the PCM driver to perform the
- * PDC DMA operation.  All fields except dma_intr_handler() are initialized
- * by the interface.  The dms_intr_handler() pointer is set by the PCM
- * driver and called by the interface SSC interrupt handler if it is
- * non-NULL.
- */
-struct at32_pcm_dma_params {
-       char *name;             /* stream identifier */
-       int pdc_xfer_size;      /* PDC counter increment in bytes */
-       struct ssc_device *ssc; /* SSC device for stream */
-       struct at32_pdc_regs *pdc;      /* PDC register info */
-       struct at32_ssc_mask *mask;     /* SSC mask info */
-       struct snd_pcm_substream *substream;
-       void (*dma_intr_handler) (u32, struct snd_pcm_substream *);
-};
-
-
-
-/*
- * The AT32 ASoC platform driver
- */
-extern struct snd_soc_platform at32_soc_platform;
-
-
-
-/*
- * SSC register access (since ssc_writel() / ssc_readl() require literal name)
- */
-#define ssc_readx(base, reg)            (__raw_readl((base) + (reg)))
-#define ssc_writex(base, reg, value)    __raw_writel((value), (base) + (reg))
-
-#endif /* __SOUND_SOC_AT32_AT32_PCM_H */
diff --git a/sound/soc/at32/at32-ssc.c b/sound/soc/at32/at32-ssc.c
deleted file mode 100644 (file)
index 4ef6492..0000000
+++ /dev/null
@@ -1,849 +0,0 @@
-/* sound/soc/at32/at32-ssc.c
- * ASoC platform driver for AT32 using SSC as DAI
- *
- * Copyright (C) 2008 Long Range Systems
- *    Geoffrey Wossum <gwossum@acm.org>
- *
- * 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.
- *
- * Note that this is basically a port of the sound/soc/at91-ssc.c to
- * the AVR32 kernel.  Thanks to Frank Mandarino for that code.
- */
-
-/* #define DEBUG */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/atmel_pdc.h>
-#include <linux/atmel-ssc.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-
-#include "at32-pcm.h"
-#include "at32-ssc.h"
-
-
-
-/*-------------------------------------------------------------------------*\
- * Constants
-\*-------------------------------------------------------------------------*/
-#define NUM_SSC_DEVICES                3
-
-/*
- * SSC direction masks
- */
-#define SSC_DIR_MASK_UNUSED    0
-#define SSC_DIR_MASK_PLAYBACK  1
-#define SSC_DIR_MASK_CAPTURE   2
-
-/*
- * SSC register values that Atmel left out of <linux/atmel-ssc.h>.  These
- * are expected to be used with SSC_BF
- */
-/* START bit field values */
-#define SSC_START_CONTINUOUS   0
-#define SSC_START_TX_RX                1
-#define SSC_START_LOW_RF       2
-#define SSC_START_HIGH_RF      3
-#define SSC_START_FALLING_RF   4
-#define SSC_START_RISING_RF    5
-#define SSC_START_LEVEL_RF     6
-#define SSC_START_EDGE_RF      7
-#define SSS_START_COMPARE_0    8
-
-/* CKI bit field values */
-#define SSC_CKI_FALLING                0
-#define SSC_CKI_RISING         1
-
-/* CKO bit field values */
-#define SSC_CKO_NONE           0
-#define SSC_CKO_CONTINUOUS     1
-#define SSC_CKO_TRANSFER       2
-
-/* CKS bit field values */
-#define SSC_CKS_DIV            0
-#define SSC_CKS_CLOCK          1
-#define SSC_CKS_PIN            2
-
-/* FSEDGE bit field values */
-#define SSC_FSEDGE_POSITIVE    0
-#define SSC_FSEDGE_NEGATIVE    1
-
-/* FSOS bit field values */
-#define SSC_FSOS_NONE          0
-#define SSC_FSOS_NEGATIVE      1
-#define SSC_FSOS_POSITIVE      2
-#define SSC_FSOS_LOW           3
-#define SSC_FSOS_HIGH          4
-#define SSC_FSOS_TOGGLE                5
-
-#define START_DELAY            1
-
-
-
-/*-------------------------------------------------------------------------*\
- * Module data
-\*-------------------------------------------------------------------------*/
-/*
- * SSC PDC registered required by the PCM DMA engine
- */
-static struct at32_pdc_regs pdc_tx_reg = {
-       .xpr = SSC_PDC_TPR,
-       .xcr = SSC_PDC_TCR,
-       .xnpr = SSC_PDC_TNPR,
-       .xncr = SSC_PDC_TNCR,
-};
-
-
-
-static struct at32_pdc_regs pdc_rx_reg = {
-       .xpr = SSC_PDC_RPR,
-       .xcr = SSC_PDC_RCR,
-       .xnpr = SSC_PDC_RNPR,
-       .xncr = SSC_PDC_RNCR,
-};
-
-
-
-/*
- * SSC and PDC status bits for transmit and receive
- */
-static struct at32_ssc_mask ssc_tx_mask = {
-       .ssc_enable = SSC_BIT(CR_TXEN),
-       .ssc_disable = SSC_BIT(CR_TXDIS),
-       .ssc_endx = SSC_BIT(SR_ENDTX),
-       .ssc_endbuf = SSC_BIT(SR_TXBUFE),
-       .pdc_enable = SSC_BIT(PDC_PTCR_TXTEN),
-       .pdc_disable = SSC_BIT(PDC_PTCR_TXTDIS),
-};
-
-
-
-static struct at32_ssc_mask ssc_rx_mask = {
-       .ssc_enable = SSC_BIT(CR_RXEN),
-       .ssc_disable = SSC_BIT(CR_RXDIS),
-       .ssc_endx = SSC_BIT(SR_ENDRX),
-       .ssc_endbuf = SSC_BIT(SR_RXBUFF),
-       .pdc_enable = SSC_BIT(PDC_PTCR_RXTEN),
-       .pdc_disable = SSC_BIT(PDC_PTCR_RXTDIS),
-};
-
-
-
-/*
- * DMA parameters for each SSC
- */
-static struct at32_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
-       {
-        {
-         .name = "SSC0 PCM out",
-         .pdc = &pdc_tx_reg,
-         .mask = &ssc_tx_mask,
-         },
-        {
-         .name = "SSC0 PCM in",
-         .pdc = &pdc_rx_reg,
-         .mask = &ssc_rx_mask,
-         },
-        },
-       {
-        {
-         .name = "SSC1 PCM out",
-         .pdc = &pdc_tx_reg,
-         .mask = &ssc_tx_mask,
-         },
-        {
-         .name = "SSC1 PCM in",
-         .pdc = &pdc_rx_reg,
-         .mask = &ssc_rx_mask,
-         },
-        },
-       {
-        {
-         .name = "SSC2 PCM out",
-         .pdc = &pdc_tx_reg,
-         .mask = &ssc_tx_mask,
-         },
-        {
-         .name = "SSC2 PCM in",
-         .pdc = &pdc_rx_reg,
-         .mask = &ssc_rx_mask,
-         },
-        },
-};
-
-
-
-static struct at32_ssc_info ssc_info[NUM_SSC_DEVICES] = {
-       {
-        .name = "ssc0",
-        .lock = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
-        .dir_mask = SSC_DIR_MASK_UNUSED,
-        .initialized = 0,
-        },
-       {
-        .name = "ssc1",
-        .lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
-        .dir_mask = SSC_DIR_MASK_UNUSED,
-        .initialized = 0,
-        },
-       {
-        .name = "ssc2",
-        .lock = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
-        .dir_mask = SSC_DIR_MASK_UNUSED,
-        .initialized = 0,
-        },
-};
-
-
-
-
-/*-------------------------------------------------------------------------*\
- * ISR
-\*-------------------------------------------------------------------------*/
-/*
- * SSC interrupt handler.  Passes PDC interrupts to the DMA interrupt
- * handler in the PCM driver.
- */
-static irqreturn_t at32_ssc_interrupt(int irq, void *dev_id)
-{
-       struct at32_ssc_info *ssc_p = dev_id;
-       struct at32_pcm_dma_params *dma_params;
-       u32 ssc_sr;
-       u32 ssc_substream_mask;
-       int i;
-
-       ssc_sr = (ssc_readl(ssc_p->ssc->regs, SR) &
-                 ssc_readl(ssc_p->ssc->regs, IMR));
-
-       /*
-        * Loop through substreams attached to this SSC.  If a DMA-related
-        * interrupt occured on that substream, call the DMA interrupt
-        * handler function, if one has been registered in the dma_param
-        * structure by the PCM driver.
-        */
-       for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
-               dma_params = ssc_p->dma_params[i];
-
-               if ((dma_params != NULL) &&
-                   (dma_params->dma_intr_handler != NULL)) {
-                       ssc_substream_mask = (dma_params->mask->ssc_endx |
-                                             dma_params->mask->ssc_endbuf);
-                       if (ssc_sr & ssc_substream_mask) {
-                               dma_params->dma_intr_handler(ssc_sr,
-                                                            dma_params->
-                                                            substream);
-                       }
-               }
-       }
-
-
-       return IRQ_HANDLED;
-}
-
-/*-------------------------------------------------------------------------*\
- * DAI functions
-\*-------------------------------------------------------------------------*/
-/*
- * Startup.  Only that one substream allowed in each direction.
- */
-static int at32_ssc_startup(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
-       int dir_mask;
-
-       dir_mask = ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
-                   SSC_DIR_MASK_PLAYBACK : SSC_DIR_MASK_CAPTURE);
-
-       spin_lock_irq(&ssc_p->lock);
-       if (ssc_p->dir_mask & dir_mask) {
-               spin_unlock_irq(&ssc_p->lock);
-               return -EBUSY;
-       }
-       ssc_p->dir_mask |= dir_mask;
-       spin_unlock_irq(&ssc_p->lock);
-
-       return 0;
-}
-
-
-
-/*
- * Shutdown.  Clear DMA parameters and shutdown the SSC if there
- * are no other substreams open.
- */
-static void at32_ssc_shutdown(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
-       struct at32_pcm_dma_params *dma_params;
-       int dir_mask;
-
-       dma_params = ssc_p->dma_params[substream->stream];
-
-       if (dma_params != NULL) {
-               ssc_writel(dma_params->ssc->regs, CR,
-                          dma_params->mask->ssc_disable);
-               pr_debug("%s disabled SSC_SR=0x%08x\n",
-                        (substream->stream ? "receiver" : "transmit"),
-                        ssc_readl(ssc_p->ssc->regs, SR));
-
-               dma_params->ssc = NULL;
-               dma_params->substream = NULL;
-               ssc_p->dma_params[substream->stream] = NULL;
-       }
-
-
-       dir_mask = 1 << substream->stream;
-       spin_lock_irq(&ssc_p->lock);
-       ssc_p->dir_mask &= ~dir_mask;
-       if (!ssc_p->dir_mask) {
-               /* Shutdown the SSC clock */
-               pr_debug("at32-ssc: Stopping user %d clock\n",
-                        ssc_p->ssc->user);
-               clk_disable(ssc_p->ssc->clk);
-
-               if (ssc_p->initialized) {
-                       free_irq(ssc_p->ssc->irq, ssc_p);
-                       ssc_p->initialized = 0;
-               }
-
-               /* Reset the SSC */
-               ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
-
-               /* clear the SSC dividers */
-               ssc_p->cmr_div = 0;
-               ssc_p->tcmr_period = 0;
-               ssc_p->rcmr_period = 0;
-       }
-       spin_unlock_irq(&ssc_p->lock);
-}
-
-
-
-/*
- * Set the SSC system clock rate
- */
-static int at32_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
-                                  int clk_id, unsigned int freq, int dir)
-{
-       /* TODO: What the heck do I do here? */
-       return 0;
-}
-
-
-
-/*
- * Record DAI format for use by hw_params()
- */
-static int at32_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
-                               unsigned int fmt)
-{
-       struct at32_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
-
-       ssc_p->daifmt = fmt;
-       return 0;
-}
-
-
-
-/*
- * Record SSC clock dividers for use in hw_params()
- */
-static int at32_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
-                                  int div_id, int div)
-{
-       struct at32_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
-
-       switch (div_id) {
-       case AT32_SSC_CMR_DIV:
-               /*
-                * The same master clock divider is used for both
-                * transmit and receive, so if a value has already
-                * been set, it must match this value
-                */
-               if (ssc_p->cmr_div == 0)
-                       ssc_p->cmr_div = div;
-               else if (div != ssc_p->cmr_div)
-                       return -EBUSY;
-               break;
-
-       case AT32_SSC_TCMR_PERIOD:
-               ssc_p->tcmr_period = div;
-               break;
-
-       case AT32_SSC_RCMR_PERIOD:
-               ssc_p->rcmr_period = div;
-               break;
-
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-
-
-/*
- * Configure the SSC
- */
-static int at32_ssc_hw_params(struct snd_pcm_substream *substream,
-                             struct snd_pcm_hw_params *params)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       int id = rtd->dai->cpu_dai->id;
-       struct at32_ssc_info *ssc_p = &ssc_info[id];
-       struct at32_pcm_dma_params *dma_params;
-       int channels, bits;
-       u32 tfmr, rfmr, tcmr, rcmr;
-       int start_event;
-       int ret;
-
-
-       /*
-        * Currently, there is only one set of dma_params for each direction.
-        * If more are added, this code will have to be changed to select
-        * the proper set
-        */
-       dma_params = &ssc_dma_params[id][substream->stream];
-       dma_params->ssc = ssc_p->ssc;
-       dma_params->substream = substream;
-
-       ssc_p->dma_params[substream->stream] = dma_params;
-
-
-       /*
-        * The cpu_dai->dma_data field is only used to communicate the
-        * appropriate DMA parameters to the PCM driver's hw_params()
-        * function.  It should not be used for other purposes as it
-        * is common to all substreams.
-        */
-       rtd->dai->cpu_dai->dma_data = dma_params;
-
-       channels = params_channels(params);
-
-
-       /*
-        * Determine sample size in bits and the PDC increment
-        */
-       switch (params_format(params)) {
-       case SNDRV_PCM_FORMAT_S8:
-               bits = 8;
-               dma_params->pdc_xfer_size = 1;
-               break;
-
-       case SNDRV_PCM_FORMAT_S16:
-               bits = 16;
-               dma_params->pdc_xfer_size = 2;
-               break;
-
-       case SNDRV_PCM_FORMAT_S24:
-               bits = 24;
-               dma_params->pdc_xfer_size = 4;
-               break;
-
-       case SNDRV_PCM_FORMAT_S32:
-               bits = 32;
-               dma_params->pdc_xfer_size = 4;
-               break;
-
-       default:
-               pr_warning("at32-ssc: Unsupported PCM format %d",
-                          params_format(params));
-               return -EINVAL;
-       }
-       pr_debug("at32-ssc: bits = %d, pdc_xfer_size = %d, channels = %d\n",
-                bits, dma_params->pdc_xfer_size, channels);
-
-
-       /*
-        * The SSC only supports up to 16-bit samples in I2S format, due
-        * to the size of the Frame Mode Register FSLEN field.
-        */
-       if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
-               if (bits > 16) {
-                       pr_warning("at32-ssc: "
-                                  "sample size %d is too large for I2S\n",
-                                  bits);
-                       return -EINVAL;
-               }
-
-
-       /*
-        * Compute the SSC register settings
-        */
-       switch (ssc_p->daifmt & (SND_SOC_DAIFMT_FORMAT_MASK |
-                                SND_SOC_DAIFMT_MASTER_MASK)) {
-       case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
-               /*
-                * I2S format, SSC provides BCLK and LRS clocks.
-                *
-                * The SSC transmit and receive clocks are generated from the
-                * MCK divider, and the BCLK signal is output on the SSC TK line
-                */
-               pr_debug("at32-ssc: SSC mode is I2S BCLK / FRAME master\n");
-               rcmr = (SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) |
-                       SSC_BF(RCMR_STTDLY, START_DELAY) |
-                       SSC_BF(RCMR_START, SSC_START_FALLING_RF) |
-                       SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
-                       SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
-                       SSC_BF(RCMR_CKS, SSC_CKS_DIV));
-
-               rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
-                       SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE) |
-                       SSC_BF(RFMR_FSLEN, bits - 1) |
-                       SSC_BF(RFMR_DATNB, channels - 1) |
-                       SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
-
-               tcmr = (SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) |
-                       SSC_BF(TCMR_STTDLY, START_DELAY) |
-                       SSC_BF(TCMR_START, SSC_START_FALLING_RF) |
-                       SSC_BF(TCMR_CKI, SSC_CKI_FALLING) |
-                       SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) |
-                       SSC_BF(TCMR_CKS, SSC_CKS_DIV));
-
-               tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
-                       SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE) |
-                       SSC_BF(TFMR_FSLEN, bits - 1) |
-                       SSC_BF(TFMR_DATNB, channels - 1) | SSC_BIT(TFMR_MSBF) |
-                       SSC_BF(TFMR_DATLEN, bits - 1));
-               break;
-
-
-       case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
-               /*
-                * I2S format, CODEC supplies BCLK and LRC clock.
-                *
-                * The SSC transmit clock is obtained from the BCLK signal
-                * on the TK line, and the SSC receive clock is generated from
-                * the transmit clock.
-                *
-                * For single channel data, one sample is transferred on the
-                * falling edge of the LRC clock.  For two channel data, one
-                * sample is transferred on both edges of the LRC clock.
-                */
-               pr_debug("at32-ssc: SSC mode is I2S BCLK / FRAME slave\n");
-               start_event = ((channels == 1) ?
-                              SSC_START_FALLING_RF : SSC_START_EDGE_RF);
-
-               rcmr = (SSC_BF(RCMR_STTDLY, START_DELAY) |
-                       SSC_BF(RCMR_START, start_event) |
-                       SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
-                       SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
-                       SSC_BF(RCMR_CKS, SSC_CKS_CLOCK));
-
-               rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
-                       SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) |
-                       SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
-
-               tcmr = (SSC_BF(TCMR_STTDLY, START_DELAY) |
-                       SSC_BF(TCMR_START, start_event) |
-                       SSC_BF(TCMR_CKI, SSC_CKI_FALLING) |
-                       SSC_BF(TCMR_CKO, SSC_CKO_NONE) |
-                       SSC_BF(TCMR_CKS, SSC_CKS_PIN));
-
-               tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
-                       SSC_BF(TFMR_FSOS, SSC_FSOS_NONE) |
-                       SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATLEN, bits - 1));
-               break;
-
-
-       case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
-               /*
-                * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
-                *
-                * The SSC transmit and receive clocks are generated from the
-                * MCK divider, and the BCLK signal is output on the SSC TK line
-                */
-               pr_debug("at32-ssc: SSC mode is DSP A BCLK / FRAME master\n");
-               rcmr = (SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) |
-                       SSC_BF(RCMR_STTDLY, 1) |
-                       SSC_BF(RCMR_START, SSC_START_RISING_RF) |
-                       SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
-                       SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
-                       SSC_BF(RCMR_CKS, SSC_CKS_DIV));
-
-               rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
-                       SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE) |
-                       SSC_BF(RFMR_DATNB, channels - 1) |
-                       SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
-
-               tcmr = (SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) |
-                       SSC_BF(TCMR_STTDLY, 1) |
-                       SSC_BF(TCMR_START, SSC_START_RISING_RF) |
-                       SSC_BF(TCMR_CKI, SSC_CKI_RISING) |
-                       SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) |
-                       SSC_BF(TCMR_CKS, SSC_CKS_DIV));
-
-               tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
-                       SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE) |
-                       SSC_BF(TFMR_DATNB, channels - 1) |
-                       SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATLEN, bits - 1));
-               break;
-
-
-       case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
-       default:
-               pr_warning("at32-ssc: unsupported DAI format 0x%x\n",
-                          ssc_p->daifmt);
-               return -EINVAL;
-               break;
-       }
-       pr_debug("at32-ssc: RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
-                rcmr, rfmr, tcmr, tfmr);
-
-
-       if (!ssc_p->initialized) {
-               /* enable peripheral clock */
-               pr_debug("at32-ssc: Starting clock\n");
-               clk_enable(ssc_p->ssc->clk);
-
-               /* Reset the SSC and its PDC registers */
-               ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
-
-               ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0);
-               ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0);
-               ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0);
-               ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0);
-
-               ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0);
-               ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0);
-               ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0);
-               ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0);
-
-               ret = request_irq(ssc_p->ssc->irq, at32_ssc_interrupt, 0,
-                                 ssc_p->name, ssc_p);
-               if (ret < 0) {
-                       pr_warning("at32-ssc: request irq failed (%d)\n", ret);
-                       pr_debug("at32-ssc: Stopping clock\n");
-                       clk_disable(ssc_p->ssc->clk);
-                       return ret;
-               }
-
-               ssc_p->initialized = 1;
-       }
-
-       /* Set SSC clock mode register */
-       ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div);
-
-       /* set receive clock mode and format */
-       ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
-       ssc_writel(ssc_p->ssc->regs, RFMR, rfmr);
-
-       /* set transmit clock mode and format */
-       ssc_writel(ssc_p->ssc->regs, TCMR, tcmr);
-       ssc_writel(ssc_p->ssc->regs, TFMR, tfmr);
-
-       pr_debug("at32-ssc: SSC initialized\n");
-       return 0;
-}
-
-
-
-static int at32_ssc_prepare(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
-       struct at32_pcm_dma_params *dma_params;
-
-       dma_params = ssc_p->dma_params[substream->stream];
-
-       ssc_writel(dma_params->ssc->regs, CR, dma_params->mask->ssc_enable);
-
-       return 0;
-}
-
-
-
-#ifdef CONFIG_PM
-static int at32_ssc_suspend(struct platform_device *pdev,
-                           struct snd_soc_dai *cpu_dai)
-{
-       struct at32_ssc_info *ssc_p;
-
-       if (!cpu_dai->active)
-               return 0;
-
-       ssc_p = &ssc_info[cpu_dai->id];
-
-       /* Save the status register before disabling transmit and receive */
-       ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
-       ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS));
-
-       /* Save the current interrupt mask, then disable unmasked interrupts */
-       ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR);
-       ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr);
-
-       ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR);
-       ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR);
-       ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR);
-       ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR);
-       ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR);
-
-       return 0;
-}
-
-
-
-static int at32_ssc_resume(struct platform_device *pdev,
-                          struct snd_soc_dai *cpu_dai)
-{
-       struct at32_ssc_info *ssc_p;
-       u32 cr;
-
-       if (!cpu_dai->active)
-               return 0;
-
-       ssc_p = &ssc_info[cpu_dai->id];
-
-       /* restore SSC register settings */
-       ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
-       ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr);
-       ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr);
-       ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr);
-       ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr);
-
-       /* re-enable interrupts */
-       ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr);
-
-       /* Re-enable recieve and transmit as appropriate */
-       cr = 0;
-       cr |=
-           (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0;
-       cr |=
-           (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0;
-       ssc_writel(ssc_p->ssc->regs, CR, cr);
-
-       return 0;
-}
-#else /* CONFIG_PM */
-#  define at32_ssc_suspend     NULL
-#  define at32_ssc_resume      NULL
-#endif /* CONFIG_PM */
-
-
-#define AT32_SSC_RATES \
-    (SNDRV_PCM_RATE_8000  | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
-     SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
-     SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-
-
-#define AT32_SSC_FORMATS \
-    (SNDRV_PCM_FMTBIT_S8  | SNDRV_PCM_FMTBIT_S16 | \
-     SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_S32)
-
-
-struct snd_soc_dai at32_ssc_dai[NUM_SSC_DEVICES] = {
-       {
-        .name = "at32-ssc0",
-        .id = 0,
-        .type = SND_SOC_DAI_PCM,
-        .suspend = at32_ssc_suspend,
-        .resume = at32_ssc_resume,
-        .playback = {
-                     .channels_min = 1,
-                     .channels_max = 2,
-                     .rates = AT32_SSC_RATES,
-                     .formats = AT32_SSC_FORMATS,
-                     },
-        .capture = {
-                    .channels_min = 1,
-                    .channels_max = 2,
-                    .rates = AT32_SSC_RATES,
-                    .formats = AT32_SSC_FORMATS,
-                    },
-        .ops = {
-                .startup = at32_ssc_startup,
-                .shutdown = at32_ssc_shutdown,
-                .prepare = at32_ssc_prepare,
-                .hw_params = at32_ssc_hw_params,
-                },
-        .dai_ops = {
-                    .set_sysclk = at32_ssc_set_dai_sysclk,
-                    .set_fmt = at32_ssc_set_dai_fmt,
-                    .set_clkdiv = at32_ssc_set_dai_clkdiv,
-                    },
-        .private_data = &ssc_info[0],
-        },
-       {
-        .name = "at32-ssc1",
-        .id = 1,
-        .type = SND_SOC_DAI_PCM,
-        .suspend = at32_ssc_suspend,
-        .resume = at32_ssc_resume,
-        .playback = {
-                     .channels_min = 1,
-                     .channels_max = 2,
-                     .rates = AT32_SSC_RATES,
-                     .formats = AT32_SSC_FORMATS,
-                     },
-        .capture = {
-                    .channels_min = 1,
-                    .channels_max = 2,
-                    .rates = AT32_SSC_RATES,
-                    .formats = AT32_SSC_FORMATS,
-                    },
-        .ops = {
-                .startup = at32_ssc_startup,
-                .shutdown = at32_ssc_shutdown,
-                .prepare = at32_ssc_prepare,
-                .hw_params = at32_ssc_hw_params,
-                },
-        .dai_ops = {
-                    .set_sysclk = at32_ssc_set_dai_sysclk,
-                    .set_fmt = at32_ssc_set_dai_fmt,
-                    .set_clkdiv = at32_ssc_set_dai_clkdiv,
-                    },
-        .private_data = &ssc_info[1],
-        },
-       {
-        .name = "at32-ssc2",
-        .id = 2,
-        .type = SND_SOC_DAI_PCM,
-        .suspend = at32_ssc_suspend,
-        .resume = at32_ssc_resume,
-        .playback = {
-                     .channels_min = 1,
-                     .channels_max = 2,
-                     .rates = AT32_SSC_RATES,
-                     .formats = AT32_SSC_FORMATS,
-                     },
-        .capture = {
-                    .channels_min = 1,
-                    .channels_max = 2,
-                    .rates = AT32_SSC_RATES,
-                    .formats = AT32_SSC_FORMATS,
-                    },
-        .ops = {
-                .startup = at32_ssc_startup,
-                .shutdown = at32_ssc_shutdown,
-                .prepare = at32_ssc_prepare,
-                .hw_params = at32_ssc_hw_params,
-                },
-        .dai_ops = {
-                    .set_sysclk = at32_ssc_set_dai_sysclk,
-                    .set_fmt = at32_ssc_set_dai_fmt,
-                    .set_clkdiv = at32_ssc_set_dai_clkdiv,
-                    },
-        .private_data = &ssc_info[2],
-        },
-};
-EXPORT_SYMBOL_GPL(at32_ssc_dai);
-
-
-MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
-MODULE_DESCRIPTION("AT32 SSC ASoC Interface");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/at32/at32-ssc.h b/sound/soc/at32/at32-ssc.h
deleted file mode 100644 (file)
index 3c052db..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/* sound/soc/at32/at32-ssc.h
- * ASoC SSC interface for Atmel AT32 SoC
- *
- * Copyright (C) 2008 Long Range Systems
- *    Geoffrey Wossum <gwossum@acm.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef __SOUND_SOC_AT32_AT32_SSC_H
-#define __SOUND_SOC_AT32_AT32_SSC_H __FILE__
-
-#include <linux/types.h>
-#include <linux/atmel-ssc.h>
-
-#include "at32-pcm.h"
-
-
-
-struct at32_ssc_state {
-       u32 ssc_cmr;
-       u32 ssc_rcmr;
-       u32 ssc_rfmr;
-       u32 ssc_tcmr;
-       u32 ssc_tfmr;
-       u32 ssc_sr;
-       u32 ssc_imr;
-};
-
-
-
-struct at32_ssc_info {
-       char *name;
-       struct ssc_device *ssc;
-       spinlock_t lock;        /* lock for dir_mask */
-       unsigned short dir_mask;        /* 0=unused, 1=playback, 2=capture */
-       unsigned short initialized;     /* true if SSC has been initialized */
-       unsigned short daifmt;
-       unsigned short cmr_div;
-       unsigned short tcmr_period;
-       unsigned short rcmr_period;
-       struct at32_pcm_dma_params *dma_params[2];
-       struct at32_ssc_state ssc_state;
-};
-
-
-/* SSC divider ids */
-#define AT32_SSC_CMR_DIV        0      /* MCK divider for BCLK */
-#define AT32_SSC_TCMR_PERIOD    1      /* BCLK divider for transmit FS */
-#define AT32_SSC_RCMR_PERIOD    2      /* BCLK divider for receive FS */
-
-
-extern struct snd_soc_dai at32_ssc_dai[];
-
-
-
-#endif /* __SOUND_SOC_AT32_AT32_SSC_H */
diff --git a/sound/soc/at91/Kconfig b/sound/soc/at91/Kconfig
deleted file mode 100644 (file)
index 85a8832..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-config SND_AT91_SOC
-       tristate "SoC Audio for the Atmel AT91 System-on-Chip"
-       depends on ARCH_AT91
-       help
-         Say Y or M if you want to add support for codecs attached to
-         the AT91 SSC interface. You will also need
-         to select the audio interfaces to support below.
-
-config SND_AT91_SOC_SSC
-       tristate
diff --git a/sound/soc/at91/Makefile b/sound/soc/at91/Makefile
deleted file mode 100644 (file)
index b817f11..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# AT91 Platform Support
-snd-soc-at91-objs := at91-pcm.o
-snd-soc-at91-ssc-objs := at91-ssc.o
-
-obj-$(CONFIG_SND_AT91_SOC) += snd-soc-at91.o
-obj-$(CONFIG_SND_AT91_SOC_SSC) += snd-soc-at91-ssc.o
diff --git a/sound/soc/at91/at91-pcm.c b/sound/soc/at91/at91-pcm.c
deleted file mode 100644 (file)
index 7ab48bd..0000000
+++ /dev/null
@@ -1,434 +0,0 @@
-/*
- * at91-pcm.c -- ALSA PCM interface for the Atmel AT91 SoC
- *
- * Author:     Frank Mandarino <fmandarino@endrelia.com>
- *             Endrelia Technologies Inc.
- * Created:    Mar 3, 2006
- *
- * Based on pxa2xx-pcm.c by:
- *
- * Author:     Nicolas Pitre
- * Created:    Nov 30, 2004
- * Copyright:  (C) 2004 MontaVista Software, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-#include <linux/atmel_pdc.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <mach/hardware.h>
-#include <mach/at91_ssc.h>
-
-#include "at91-pcm.h"
-
-#if 0
-#define        DBG(x...)       printk(KERN_INFO "at91-pcm: " x)
-#else
-#define        DBG(x...)
-#endif
-
-static const struct snd_pcm_hardware at91_pcm_hardware = {
-       .info                   = SNDRV_PCM_INFO_MMAP |
-                                 SNDRV_PCM_INFO_MMAP_VALID |
-                                 SNDRV_PCM_INFO_INTERLEAVED |
-                                 SNDRV_PCM_INFO_PAUSE,
-       .formats                = SNDRV_PCM_FMTBIT_S16_LE,
-       .period_bytes_min       = 32,
-       .period_bytes_max       = 8192,
-       .periods_min            = 2,
-       .periods_max            = 1024,
-       .buffer_bytes_max       = 32 * 1024,
-};
-
-struct at91_runtime_data {
-       struct at91_pcm_dma_params *params;
-       dma_addr_t dma_buffer;                  /* physical address of dma buffer */
-       dma_addr_t dma_buffer_end;              /* first address beyond DMA buffer */
-       size_t period_size;
-       dma_addr_t period_ptr;                  /* physical address of next period */
-       u32 pdc_xpr_save;                       /* PDC register save */
-       u32 pdc_xcr_save;
-       u32 pdc_xnpr_save;
-       u32 pdc_xncr_save;
-};
-
-static void at91_pcm_dma_irq(u32 ssc_sr,
-       struct snd_pcm_substream *substream)
-{
-       struct at91_runtime_data *prtd = substream->runtime->private_data;
-       struct at91_pcm_dma_params *params = prtd->params;
-       static int count = 0;
-
-       count++;
-
-       if (ssc_sr & params->mask->ssc_endbuf) {
-
-               printk(KERN_WARNING
-                       "at91-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
-                       substream->stream == SNDRV_PCM_STREAM_PLAYBACK
-                               ? "underrun" : "overrun",
-                       params->name, ssc_sr, count);
-
-               /* re-start the PDC */
-               at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
-
-               prtd->period_ptr += prtd->period_size;
-               if (prtd->period_ptr >= prtd->dma_buffer_end) {
-                       prtd->period_ptr = prtd->dma_buffer;
-               }
-
-               at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->period_ptr);
-               at91_ssc_write(params->ssc_base + params->pdc->xcr,
-                               prtd->period_size / params->pdc_xfer_size);
-
-               at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_enable);
-       }
-
-       if (ssc_sr & params->mask->ssc_endx) {
-
-               /* Load the PDC next pointer and counter registers */
-               prtd->period_ptr += prtd->period_size;
-               if (prtd->period_ptr >= prtd->dma_buffer_end) {
-                       prtd->period_ptr = prtd->dma_buffer;
-               }
-               at91_ssc_write(params->ssc_base + params->pdc->xnpr,
-                              prtd->period_ptr);
-               at91_ssc_write(params->ssc_base + params->pdc->xncr,
-                               prtd->period_size / params->pdc_xfer_size);
-       }
-
-       snd_pcm_period_elapsed(substream);
-}
-
-static int at91_pcm_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct at91_runtime_data *prtd = runtime->private_data;
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
-       /* this may get called several times by oss emulation
-        * with different params */
-
-       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-       runtime->dma_bytes = params_buffer_bytes(params);
-
-       prtd->params = rtd->dai->cpu_dai->dma_data;
-       prtd->params->dma_intr_handler = at91_pcm_dma_irq;
-
-       prtd->dma_buffer = runtime->dma_addr;
-       prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
-       prtd->period_size = params_period_bytes(params);
-
-       DBG("hw_params: DMA for %s initialized (dma_bytes=%d, period_size=%d)\n",
-               prtd->params->name, runtime->dma_bytes, prtd->period_size);
-       return 0;
-}
-
-static int at91_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-       struct at91_runtime_data *prtd = substream->runtime->private_data;
-       struct at91_pcm_dma_params *params = prtd->params;
-
-       if (params != NULL) {
-               at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
-               prtd->params->dma_intr_handler = NULL;
-       }
-
-       return 0;
-}
-
-static int at91_pcm_prepare(struct snd_pcm_substream *substream)
-{
-       struct at91_runtime_data *prtd = substream->runtime->private_data;
-       struct at91_pcm_dma_params *params = prtd->params;
-
-       at91_ssc_write(params->ssc_base + AT91_SSC_IDR,
-                       params->mask->ssc_endx | params->mask->ssc_endbuf);
-
-       at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
-       return 0;
-}
-
-static int at91_pcm_trigger(struct snd_pcm_substream *substream,
-       int cmd)
-{
-       struct at91_runtime_data *prtd = substream->runtime->private_data;
-       struct at91_pcm_dma_params *params = prtd->params;
-       int ret = 0;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-               prtd->period_ptr = prtd->dma_buffer;
-
-               at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->period_ptr);
-               at91_ssc_write(params->ssc_base + params->pdc->xcr,
-                               prtd->period_size / params->pdc_xfer_size);
-
-               prtd->period_ptr += prtd->period_size;
-               at91_ssc_write(params->ssc_base + params->pdc->xnpr, prtd->period_ptr);
-               at91_ssc_write(params->ssc_base + params->pdc->xncr,
-                               prtd->period_size / params->pdc_xfer_size);
-
-               DBG("trigger: period_ptr=%lx, xpr=%lx, xcr=%ld, xnpr=%lx, xncr=%ld\n",
-                       (unsigned long) prtd->period_ptr,
-                       at91_ssc_read(params->ssc_base + params->pdc->xpr),
-                       at91_ssc_read(params->ssc_base + params->pdc->xcr),
-                       at91_ssc_read(params->ssc_base + params->pdc->xnpr),
-                       at91_ssc_read(params->ssc_base + params->pdc->xncr));
-
-               at91_ssc_write(params->ssc_base + AT91_SSC_IER,
-                       params->mask->ssc_endx | params->mask->ssc_endbuf);
-
-               at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR,
-                       params->mask->pdc_enable);
-
-               DBG("sr=%lx imr=%lx\n",
-                   at91_ssc_read(params->ssc_base + AT91_SSC_SR),
-                   at91_ssc_read(params->ssc_base + AT91_SSC_IMR));
-               break;
-
-       case SNDRV_PCM_TRIGGER_STOP:
-       case SNDRV_PCM_TRIGGER_SUSPEND:
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
-               break;
-
-       case SNDRV_PCM_TRIGGER_RESUME:
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_enable);
-               break;
-
-       default:
-               ret = -EINVAL;
-       }
-
-       return ret;
-}
-
-static snd_pcm_uframes_t at91_pcm_pointer(
-       struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct at91_runtime_data *prtd = runtime->private_data;
-       struct at91_pcm_dma_params *params = prtd->params;
-       dma_addr_t ptr;
-       snd_pcm_uframes_t x;
-
-       ptr = (dma_addr_t) at91_ssc_read(params->ssc_base + params->pdc->xpr);
-       x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
-
-       if (x == runtime->buffer_size)
-               x = 0;
-       return x;
-}
-
-static int at91_pcm_open(struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct at91_runtime_data *prtd;
-       int ret = 0;
-
-       snd_soc_set_runtime_hwparams(substream, &at91_pcm_hardware);
-
-       /* ensure that buffer size is a multiple of period size */
-       ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
-       if (ret < 0)
-               goto out;
-
-       prtd = kzalloc(sizeof(struct at91_runtime_data), GFP_KERNEL);
-       if (prtd == NULL) {
-               ret = -ENOMEM;
-               goto out;
-       }
-       runtime->private_data = prtd;
-
- out:
-       return ret;
-}
-
-static int at91_pcm_close(struct snd_pcm_substream *substream)
-{
-       struct at91_runtime_data *prtd = substream->runtime->private_data;
-
-       kfree(prtd);
-       return 0;
-}
-
-static int at91_pcm_mmap(struct snd_pcm_substream *substream,
-       struct vm_area_struct *vma)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-
-       return dma_mmap_writecombine(substream->pcm->card->dev, vma,
-                                    runtime->dma_area,
-                                    runtime->dma_addr,
-                                    runtime->dma_bytes);
-}
-
-struct snd_pcm_ops at91_pcm_ops = {
-       .open           = at91_pcm_open,
-       .close          = at91_pcm_close,
-       .ioctl          = snd_pcm_lib_ioctl,
-       .hw_params      = at91_pcm_hw_params,
-       .hw_free        = at91_pcm_hw_free,
-       .prepare        = at91_pcm_prepare,
-       .trigger        = at91_pcm_trigger,
-       .pointer        = at91_pcm_pointer,
-       .mmap           = at91_pcm_mmap,
-};
-
-static int at91_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
-       int stream)
-{
-       struct snd_pcm_substream *substream = pcm->streams[stream].substream;
-       struct snd_dma_buffer *buf = &substream->dma_buffer;
-       size_t size = at91_pcm_hardware.buffer_bytes_max;
-
-       buf->dev.type = SNDRV_DMA_TYPE_DEV;
-       buf->dev.dev = pcm->card->dev;
-       buf->private_data = NULL;
-       buf->area = dma_alloc_writecombine(pcm->card->dev, size,
-                                          &buf->addr, GFP_KERNEL);
-
-       DBG("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
-               (void *) buf->area,
-               (void *) buf->addr,
-               size);
-
-       if (!buf->area)
-               return -ENOMEM;
-
-       buf->bytes = size;
-       return 0;
-}
-
-static u64 at91_pcm_dmamask = 0xffffffff;
-
-static int at91_pcm_new(struct snd_card *card,
-       struct snd_soc_dai *dai, struct snd_pcm *pcm)
-{
-       int ret = 0;
-
-       if (!card->dev->dma_mask)
-               card->dev->dma_mask = &at91_pcm_dmamask;
-       if (!card->dev->coherent_dma_mask)
-               card->dev->coherent_dma_mask = 0xffffffff;
-
-       if (dai->playback.channels_min) {
-               ret = at91_pcm_preallocate_dma_buffer(pcm,
-                       SNDRV_PCM_STREAM_PLAYBACK);
-               if (ret)
-                       goto out;
-       }
-
-       if (dai->capture.channels_min) {
-               ret = at91_pcm_preallocate_dma_buffer(pcm,
-                       SNDRV_PCM_STREAM_CAPTURE);
-               if (ret)
-                       goto out;
-       }
- out:
-       return ret;
-}
-
-static void at91_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
-       struct snd_pcm_substream *substream;
-       struct snd_dma_buffer *buf;
-       int stream;
-
-       for (stream = 0; stream < 2; stream++) {
-               substream = pcm->streams[stream].substream;
-               if (!substream)
-                       continue;
-
-               buf = &substream->dma_buffer;
-               if (!buf->area)
-                       continue;
-
-               dma_free_writecombine(pcm->card->dev, buf->bytes,
-                                     buf->area, buf->addr);
-               buf->area = NULL;
-       }
-}
-
-#ifdef CONFIG_PM
-static int at91_pcm_suspend(struct platform_device *pdev,
-       struct snd_soc_dai *dai)
-{
-       struct snd_pcm_runtime *runtime = dai->runtime;
-       struct at91_runtime_data *prtd;
-       struct at91_pcm_dma_params *params;
-
-       if (!runtime)
-               return 0;
-
-       prtd = runtime->private_data;
-       params = prtd->params;
-
-       /* disable the PDC and save the PDC registers */
-
-       at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
-
-       prtd->pdc_xpr_save  = at91_ssc_read(params->ssc_base + params->pdc->xpr);
-       prtd->pdc_xcr_save  = at91_ssc_read(params->ssc_base + params->pdc->xcr);
-       prtd->pdc_xnpr_save = at91_ssc_read(params->ssc_base + params->pdc->xnpr);
-       prtd->pdc_xncr_save = at91_ssc_read(params->ssc_base + params->pdc->xncr);
-
-       return 0;
-}
-
-static int at91_pcm_resume(struct platform_device *pdev,
-       struct snd_soc_dai *dai)
-{
-       struct snd_pcm_runtime *runtime = dai->runtime;
-       struct at91_runtime_data *prtd;
-       struct at91_pcm_dma_params *params;
-
-       if (!runtime)
-               return 0;
-
-       prtd = runtime->private_data;
-       params = prtd->params;
-
-       /* restore the PDC registers and enable the PDC */
-       at91_ssc_write(params->ssc_base + params->pdc->xpr,  prtd->pdc_xpr_save);
-       at91_ssc_write(params->ssc_base + params->pdc->xcr,  prtd->pdc_xcr_save);
-       at91_ssc_write(params->ssc_base + params->pdc->xnpr, prtd->pdc_xnpr_save);
-       at91_ssc_write(params->ssc_base + params->pdc->xncr, prtd->pdc_xncr_save);
-
-       at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_enable);
-       return 0;
-}
-#else
-#define at91_pcm_suspend       NULL
-#define at91_pcm_resume                NULL
-#endif
-
-struct snd_soc_platform at91_soc_platform = {
-       .name           = "at91-audio",
-       .pcm_ops        = &at91_pcm_ops,
-       .pcm_new        = at91_pcm_new,
-       .pcm_free       = at91_pcm_free_dma_buffers,
-       .suspend        = at91_pcm_suspend,
-       .resume         = at91_pcm_resume,
-};
-
-EXPORT_SYMBOL_GPL(at91_soc_platform);
-
-MODULE_AUTHOR("Frank Mandarino <fmandarino@endrelia.com>");
-MODULE_DESCRIPTION("Atmel AT91 PCM module");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/at91/at91-pcm.h b/sound/soc/at91/at91-pcm.h
deleted file mode 100644 (file)
index e5aada2..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * at91-pcm.h - ALSA PCM interface for the Atmel AT91 SoC
- *
- * Author:     Frank Mandarino <fmandarino@endrelia.com>
- *             Endrelia Technologies Inc.
- * Created:    Mar 3, 2006
- *
- * Based on pxa2xx-pcm.h by:
- *
- * Author:     Nicolas Pitre
- * Created:    Nov 30, 2004
- * Copyright:  MontaVista Software, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef _AT91_PCM_H
-#define _AT91_PCM_H
-
-#include <mach/hardware.h>
-
-struct at91_ssc_periph {
-       void __iomem    *base;
-       u32             pid;
-};
-
-/*
- * Registers and status bits that are required by the PCM driver.
- */
-struct at91_pdc_regs {
-       unsigned int    xpr;            /* PDC recv/trans pointer */
-       unsigned int    xcr;            /* PDC recv/trans counter */
-       unsigned int    xnpr;           /* PDC next recv/trans pointer */
-       unsigned int    xncr;           /* PDC next recv/trans counter */
-       unsigned int    ptcr;           /* PDC transfer control */
-};
-
-struct at91_ssc_mask {
-       u32     ssc_enable;             /* SSC recv/trans enable */
-       u32     ssc_disable;            /* SSC recv/trans disable */
-       u32     ssc_endx;               /* SSC ENDTX or ENDRX */
-       u32     ssc_endbuf;             /* SSC TXBUFE or RXBUFF */
-       u32     pdc_enable;             /* PDC recv/trans enable */
-       u32     pdc_disable;            /* PDC recv/trans disable */
-};
-
-/*
- * This structure, shared between the PCM driver and the interface,
- * contains all information required by the PCM driver to perform the
- * PDC DMA operation.  All fields except dma_intr_handler() are initialized
- * by the interface.  The dms_intr_handler() pointer is set by the PCM
- * driver and called by the interface SSC interrupt handler if it is
- * non-NULL.
- */
-struct at91_pcm_dma_params {
-       char *name;                     /* stream identifier */
-       int pdc_xfer_size;              /* PDC counter increment in bytes */
-       void __iomem *ssc_base;         /* SSC base address */
-       struct at91_pdc_regs *pdc; /* PDC receive or transmit registers */
-       struct at91_ssc_mask *mask;/* SSC & PDC status bits */
-       struct snd_pcm_substream *substream;
-       void (*dma_intr_handler)(u32, struct snd_pcm_substream *);
-};
-
-extern struct snd_soc_platform at91_soc_platform;
-
-#define at91_ssc_read(a)       ((unsigned long) __raw_readl(a))
-#define at91_ssc_write(a,v)    __raw_writel((v),(a))
-
-#endif /* _AT91_PCM_H */
diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c
deleted file mode 100644 (file)
index 1b61cc4..0000000
+++ /dev/null
@@ -1,791 +0,0 @@
-/*
- * at91-ssc.c  --  ALSA SoC AT91 SSC Audio Layer Platform driver
- *
- * Author: Frank Mandarino <fmandarino@endrelia.com>
- *         Endrelia Technologies Inc.
- *
- * Based on pxa2xx Platform drivers by
- * Liam Girdwood <lrg@slimlogic.co.uk>
- *
- *  This program is free software; you can redistribute  it and/or modify it
- *  under  the terms of  the GNU General  Public License as published by the
- *  Free Software Foundation;  either version 2 of the  License, or (at your
- *  option) any later version.
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/clk.h>
-#include <linux/atmel_pdc.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-
-#include <mach/hardware.h>
-#include <mach/at91_pmc.h>
-#include <mach/at91_ssc.h>
-
-#include "at91-pcm.h"
-#include "at91-ssc.h"
-
-#if 0
-#define        DBG(x...)       printk(KERN_DEBUG "at91-ssc:" x)
-#else
-#define        DBG(x...)
-#endif
-
-#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
-#define NUM_SSC_DEVICES                1
-#else
-#define NUM_SSC_DEVICES                3
-#endif
-
-
-/*
- * SSC PDC registers required by the PCM DMA engine.
- */
-static struct at91_pdc_regs pdc_tx_reg = {
-       .xpr            = ATMEL_PDC_TPR,
-       .xcr            = ATMEL_PDC_TCR,
-       .xnpr           = ATMEL_PDC_TNPR,
-       .xncr           = ATMEL_PDC_TNCR,
-};
-
-static struct at91_pdc_regs pdc_rx_reg = {
-       .xpr            = ATMEL_PDC_RPR,
-       .xcr            = ATMEL_PDC_RCR,
-       .xnpr           = ATMEL_PDC_RNPR,
-       .xncr           = ATMEL_PDC_RNCR,
-};
-
-/*
- * SSC & PDC status bits for transmit and receive.
- */
-static struct at91_ssc_mask ssc_tx_mask = {
-       .ssc_enable     = AT91_SSC_TXEN,
-       .ssc_disable    = AT91_SSC_TXDIS,
-       .ssc_endx       = AT91_SSC_ENDTX,
-       .ssc_endbuf     = AT91_SSC_TXBUFE,
-       .pdc_enable     = ATMEL_PDC_TXTEN,
-       .pdc_disable    = ATMEL_PDC_TXTDIS,
-};
-
-static struct at91_ssc_mask ssc_rx_mask = {
-       .ssc_enable     = AT91_SSC_RXEN,
-       .ssc_disable    = AT91_SSC_RXDIS,
-       .ssc_endx       = AT91_SSC_ENDRX,
-       .ssc_endbuf     = AT91_SSC_RXBUFF,
-       .pdc_enable     = ATMEL_PDC_RXTEN,
-       .pdc_disable    = ATMEL_PDC_RXTDIS,
-};
-
-
-/*
- * DMA parameters.
- */
-static struct at91_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
-       {{
-       .name           = "SSC0 PCM out",
-       .pdc            = &pdc_tx_reg,
-       .mask           = &ssc_tx_mask,
-       },
-       {
-       .name           = "SSC0 PCM in",
-       .pdc            = &pdc_rx_reg,
-       .mask           = &ssc_rx_mask,
-       }},
-#if NUM_SSC_DEVICES == 3
-       {{
-       .name           = "SSC1 PCM out",
-       .pdc            = &pdc_tx_reg,
-       .mask           = &ssc_tx_mask,
-       },
-       {
-       .name           = "SSC1 PCM in",
-       .pdc            = &pdc_rx_reg,
-       .mask           = &ssc_rx_mask,
-       }},
-       {{
-       .name           = "SSC2 PCM out",
-       .pdc            = &pdc_tx_reg,
-       .mask           = &ssc_tx_mask,
-       },
-       {
-       .name           = "SSC2 PCM in",
-       .pdc            = &pdc_rx_reg,
-       .mask           = &ssc_rx_mask,
-       }},
-#endif
-};
-
-struct at91_ssc_state {
-       u32     ssc_cmr;
-       u32     ssc_rcmr;
-       u32     ssc_rfmr;
-       u32     ssc_tcmr;
-       u32     ssc_tfmr;
-       u32     ssc_sr;
-       u32     ssc_imr;
-};
-
-static struct at91_ssc_info {
-       char            *name;
-       struct at91_ssc_periph ssc;
-       spinlock_t      lock;           /* lock for dir_mask */
-       unsigned short  dir_mask;       /* 0=unused, 1=playback, 2=capture */
-       unsigned short  initialized;    /* 1=SSC has been initialized */
-       unsigned short  daifmt;
-       unsigned short  cmr_div;
-       unsigned short  tcmr_period;
-       unsigned short  rcmr_period;
-       struct at91_pcm_dma_params *dma_params[2];
-       struct at91_ssc_state ssc_state;
-
-} ssc_info[NUM_SSC_DEVICES] = {
-       {
-       .name           = "ssc0",
-       .lock           = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
-       .dir_mask       = 0,
-       .initialized    = 0,
-       },
-#if NUM_SSC_DEVICES == 3
-       {
-       .name           = "ssc1",
-       .lock           = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
-       .dir_mask       = 0,
-       .initialized    = 0,
-       },
-       {
-       .name           = "ssc2",
-       .lock           = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
-       .dir_mask       = 0,
-       .initialized    = 0,
-       },
-#endif
-};
-
-static unsigned int at91_ssc_sysclk;
-
-/*
- * SSC interrupt handler.  Passes PDC interrupts to the DMA
- * interrupt handler in the PCM driver.
- */
-static irqreturn_t at91_ssc_interrupt(int irq, void *dev_id)
-{
-       struct at91_ssc_info *ssc_p = dev_id;
-       struct at91_pcm_dma_params *dma_params;
-       u32 ssc_sr;
-       int i;
-
-       ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR)
-                       & at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR);
-
-       /*
-        * Loop through the substreams attached to this SSC.  If
-        * a DMA-related interrupt occurred on that substream, call
-        * the DMA interrupt handler function, if one has been
-        * registered in the dma_params structure by the PCM driver.
-        */
-       for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
-               dma_params = ssc_p->dma_params[i];
-
-               if (dma_params != NULL && dma_params->dma_intr_handler != NULL &&
-                       (ssc_sr &
-                       (dma_params->mask->ssc_endx | dma_params->mask->ssc_endbuf)))
-
-                       dma_params->dma_intr_handler(ssc_sr, dma_params->substream);
-       }
-
-       return IRQ_HANDLED;
-}
-
-/*
- * Startup.  Only that one substream allowed in each direction.
- */
-static int at91_ssc_startup(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
-       int dir_mask;
-
-       DBG("ssc_startup: SSC_SR=0x%08lx\n",
-                       at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR));
-       dir_mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0x1 : 0x2;
-
-       spin_lock_irq(&ssc_p->lock);
-       if (ssc_p->dir_mask & dir_mask) {
-               spin_unlock_irq(&ssc_p->lock);
-               return -EBUSY;
-       }
-       ssc_p->dir_mask |= dir_mask;
-       spin_unlock_irq(&ssc_p->lock);
-
-       return 0;
-}
-
-/*
- * Shutdown.  Clear DMA parameters and shutdown the SSC if there
- * are no other substreams open.
- */
-static void at91_ssc_shutdown(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
-       struct at91_pcm_dma_params *dma_params;
-       int dir, dir_mask;
-
-       dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
-       dma_params = ssc_p->dma_params[dir];
-
-       if (dma_params != NULL) {
-               at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR,
-                               dma_params->mask->ssc_disable);
-               DBG("%s disabled SSC_SR=0x%08lx\n", (dir ? "receive" : "transmit"),
-                       at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR));
-
-               dma_params->ssc_base = NULL;
-               dma_params->substream = NULL;
-               ssc_p->dma_params[dir] = NULL;
-       }
-
-       dir_mask = 1 << dir;
-
-       spin_lock_irq(&ssc_p->lock);
-       ssc_p->dir_mask &= ~dir_mask;
-       if (!ssc_p->dir_mask) {
-               /* Shutdown the SSC clock. */
-               DBG("Stopping pid %d clock\n", ssc_p->ssc.pid);
-               at91_sys_write(AT91_PMC_PCDR, 1<<ssc_p->ssc.pid);
-
-               if (ssc_p->initialized) {
-                       free_irq(ssc_p->ssc.pid, ssc_p);
-                       ssc_p->initialized = 0;
-               }
-
-               /* Reset the SSC */
-               at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST);
-
-               /* Clear the SSC dividers */
-               ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0;
-       }
-       spin_unlock_irq(&ssc_p->lock);
-}
-
-/*
- * Record the SSC system clock rate.
- */
-static int at91_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
-               int clk_id, unsigned int freq, int dir)
-{
-       /*
-        * The only clock supplied to the SSC is the AT91 master clock,
-        * which is only used if the SSC is generating BCLK and/or
-        * LRC clocks.
-        */
-       switch (clk_id) {
-       case AT91_SYSCLK_MCK:
-               at91_ssc_sysclk = freq;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-/*
- * Record the DAI format for use in hw_params().
- */
-static int at91_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
-               unsigned int fmt)
-{
-       struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
-
-       ssc_p->daifmt = fmt;
-       return 0;
-}
-
-/*
- * Record SSC clock dividers for use in hw_params().
- */
-static int at91_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
-       int div_id, int div)
-{
-       struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
-
-       switch (div_id) {
-       case AT91SSC_CMR_DIV:
-               /*
-                * The same master clock divider is used for both
-                * transmit and receive, so if a value has already
-                * been set, it must match this value.
-                */
-               if (ssc_p->cmr_div == 0)
-                       ssc_p->cmr_div = div;
-               else
-                       if (div != ssc_p->cmr_div)
-                               return -EBUSY;
-               break;
-
-       case AT91SSC_TCMR_PERIOD:
-               ssc_p->tcmr_period = div;
-               break;
-
-       case AT91SSC_RCMR_PERIOD:
-               ssc_p->rcmr_period = div;
-               break;
-
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-/*
- * Configure the SSC.
- */
-static int at91_ssc_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       int id = rtd->dai->cpu_dai->id;
-       struct at91_ssc_info *ssc_p = &ssc_info[id];
-       struct at91_pcm_dma_params *dma_params;
-       int dir, channels, bits;
-       u32 tfmr, rfmr, tcmr, rcmr;
-       int start_event;
-       int ret;
-
-       /*
-        * Currently, there is only one set of dma params for
-        * each direction.  If more are added, this code will
-        * have to be changed to select the proper set.
-        */
-       dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
-
-       dma_params = &ssc_dma_params[id][dir];
-       dma_params->ssc_base = ssc_p->ssc.base;
-       dma_params->substream = substream;
-
-       ssc_p->dma_params[dir] = dma_params;
-
-       /*
-        * The cpu_dai->dma_data field is only used to communicate the
-        * appropriate DMA parameters to the pcm driver hw_params()
-        * function.  It should not be used for other purposes
-        * as it is common to all substreams.
-        */
-       rtd->dai->cpu_dai->dma_data = dma_params;
-
-       channels = params_channels(params);
-
-       /*
-        * Determine sample size in bits and the PDC increment.
-        */
-       switch(params_format(params)) {
-       case SNDRV_PCM_FORMAT_S8:
-               bits = 8;
-               dma_params->pdc_xfer_size = 1;
-               break;
-       case SNDRV_PCM_FORMAT_S16_LE:
-               bits = 16;
-               dma_params->pdc_xfer_size = 2;
-               break;
-       case SNDRV_PCM_FORMAT_S24_LE:
-               bits = 24;
-               dma_params->pdc_xfer_size = 4;
-               break;
-       case SNDRV_PCM_FORMAT_S32_LE:
-               bits = 32;
-               dma_params->pdc_xfer_size = 4;
-               break;
-       default:
-               printk(KERN_WARNING "at91-ssc: unsupported PCM format\n");
-               return -EINVAL;
-       }
-
-       /*
-        * The SSC only supports up to 16-bit samples in I2S format, due
-        * to the size of the Frame Mode Register FSLEN field.
-        */
-       if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S
-               && bits > 16) {
-               printk(KERN_WARNING
-                       "at91-ssc: sample size %d is too large for I2S\n", bits);
-               return -EINVAL;
-       }
-
-       /*
-        * Compute SSC register settings.
-        */
-       switch (ssc_p->daifmt
-               & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
-
-       case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
-               /*
-                * I2S format, SSC provides BCLK and LRC clocks.
-                *
-                * The SSC transmit and receive clocks are generated from the
-                * MCK divider, and the BCLK signal is output on the SSC TK line.
-                */
-               rcmr =    (( ssc_p->rcmr_period         << 24) & AT91_SSC_PERIOD)
-                       | (( 1                          << 16) & AT91_SSC_STTDLY)
-                       | (( AT91_SSC_START_FALLING_RF       ) & AT91_SSC_START)
-                       | (( AT91_SSC_CK_RISING              ) & AT91_SSC_CKI)
-                       | (( AT91_SSC_CKO_NONE               ) & AT91_SSC_CKO)
-                       | (( AT91_SSC_CKS_DIV                ) & AT91_SSC_CKS);
-
-               rfmr =    (( AT91_SSC_FSEDGE_POSITIVE        ) & AT91_SSC_FSEDGE)
-                       | (( AT91_SSC_FSOS_NEGATIVE          ) & AT91_SSC_FSOS)
-                       | (((bits - 1)                  << 16) & AT91_SSC_FSLEN)
-                       | (((channels - 1)              <<  8) & AT91_SSC_DATNB)
-                       | (( 1                          <<  7) & AT91_SSC_MSBF)
-                       | (( 0                          <<  5) & AT91_SSC_LOOP)
-                       | (((bits - 1)                  <<  0) & AT91_SSC_DATALEN);
-
-               tcmr =    (( ssc_p->tcmr_period         << 24) & AT91_SSC_PERIOD)
-                       | (( 1                          << 16) & AT91_SSC_STTDLY)
-                       | (( AT91_SSC_START_FALLING_RF       ) & AT91_SSC_START)
-                       | (( AT91_SSC_CKI_FALLING            ) & AT91_SSC_CKI)
-                       | (( AT91_SSC_CKO_CONTINUOUS         ) & AT91_SSC_CKO)
-                       | (( AT91_SSC_CKS_DIV                ) & AT91_SSC_CKS);
-
-               tfmr =    (( AT91_SSC_FSEDGE_POSITIVE        ) & AT91_SSC_FSEDGE)
-                       | (( 0                          << 23) & AT91_SSC_FSDEN)
-                       | (( AT91_SSC_FSOS_NEGATIVE          ) & AT91_SSC_FSOS)
-                       | (((bits - 1)                  << 16) & AT91_SSC_FSLEN)
-                       | (((channels - 1)              <<  8) & AT91_SSC_DATNB)
-                       | (( 1                          <<  7) & AT91_SSC_MSBF)
-                       | (( 0                          <<  5) & AT91_SSC_DATDEF)
-                       | (((bits - 1)                  <<  0) & AT91_SSC_DATALEN);
-               break;
-
-       case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
-               /*
-                * I2S format, CODEC supplies BCLK and LRC clocks.
-                *
-                * The SSC transmit clock is obtained from the BCLK signal on
-                * on the TK line, and the SSC receive clock is generated from the
-                * transmit clock.
-                *
-                * For single channel data, one sample is transferred on the falling
-                * edge of the LRC clock.  For two channel data, one sample is
-                * transferred on both edges of the LRC clock.
-                */
-               start_event = channels == 1
-                               ? AT91_SSC_START_FALLING_RF
-                               : AT91_SSC_START_EDGE_RF;
-
-               rcmr =    (( 0                          << 24) & AT91_SSC_PERIOD)
-                       | (( 1                          << 16) & AT91_SSC_STTDLY)
-                       | (( start_event                     ) & AT91_SSC_START)
-                       | (( AT91_SSC_CK_RISING              ) & AT91_SSC_CKI)
-                       | (( AT91_SSC_CKO_NONE               ) & AT91_SSC_CKO)
-                       | (( AT91_SSC_CKS_CLOCK              ) & AT91_SSC_CKS);
-
-               rfmr =    (( AT91_SSC_FSEDGE_POSITIVE        ) & AT91_SSC_FSEDGE)
-                       | (( AT91_SSC_FSOS_NONE              ) & AT91_SSC_FSOS)
-                       | (( 0                          << 16) & AT91_SSC_FSLEN)
-                       | (( 0                          <<  8) & AT91_SSC_DATNB)
-                       | (( 1                          <<  7) & AT91_SSC_MSBF)
-                       | (( 0                          <<  5) & AT91_SSC_LOOP)
-                       | (((bits - 1)                  <<  0) & AT91_SSC_DATALEN);
-
-               tcmr =    (( 0                          << 24) & AT91_SSC_PERIOD)
-                       | (( 1                          << 16) & AT91_SSC_STTDLY)
-                       | (( start_event                     ) & AT91_SSC_START)
-                       | (( AT91_SSC_CKI_FALLING            ) & AT91_SSC_CKI)
-                       | (( AT91_SSC_CKO_NONE               ) & AT91_SSC_CKO)
-                       | (( AT91_SSC_CKS_PIN                ) & AT91_SSC_CKS);
-
-               tfmr =    (( AT91_SSC_FSEDGE_POSITIVE        ) & AT91_SSC_FSEDGE)
-                       | (( 0                          << 23) & AT91_SSC_FSDEN)
-                       | (( AT91_SSC_FSOS_NONE              ) & AT91_SSC_FSOS)
-                       | (( 0                          << 16) & AT91_SSC_FSLEN)
-                       | (( 0                          <<  8) & AT91_SSC_DATNB)
-                       | (( 1                          <<  7) & AT91_SSC_MSBF)
-                       | (( 0                          <<  5) & AT91_SSC_DATDEF)
-                       | (((bits - 1)                  <<  0) & AT91_SSC_DATALEN);
-               break;
-
-       case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
-               /*
-                * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
-                *
-                * The SSC transmit and receive clocks are generated from the
-                * MCK divider, and the BCLK signal is output on the SSC TK line.
-                */
-               rcmr =    (( ssc_p->rcmr_period         << 24) & AT91_SSC_PERIOD)
-                       | (( 1                          << 16) & AT91_SSC_STTDLY)
-                       | (( AT91_SSC_START_RISING_RF        ) & AT91_SSC_START)
-                       | (( AT91_SSC_CK_RISING              ) & AT91_SSC_CKI)
-                       | (( AT91_SSC_CKO_NONE               ) & AT91_SSC_CKO)
-                       | (( AT91_SSC_CKS_DIV                ) & AT91_SSC_CKS);
-
-               rfmr =    (( AT91_SSC_FSEDGE_POSITIVE        ) & AT91_SSC_FSEDGE)
-                       | (( AT91_SSC_FSOS_POSITIVE          ) & AT91_SSC_FSOS)
-                       | (( 0                          << 16) & AT91_SSC_FSLEN)
-                       | (((channels - 1)              <<  8) & AT91_SSC_DATNB)
-                       | (( 1                          <<  7) & AT91_SSC_MSBF)
-                       | (( 0                          <<  5) & AT91_SSC_LOOP)
-                       | (((bits - 1)                  <<  0) & AT91_SSC_DATALEN);
-
-               tcmr =    (( ssc_p->tcmr_period         << 24) & AT91_SSC_PERIOD)
-                       | (( 1                          << 16) & AT91_SSC_STTDLY)
-                       | (( AT91_SSC_START_RISING_RF        ) & AT91_SSC_START)
-                       | (( AT91_SSC_CK_RISING              ) & AT91_SSC_CKI)
-                       | (( AT91_SSC_CKO_CONTINUOUS         ) & AT91_SSC_CKO)
-                       | (( AT91_SSC_CKS_DIV                ) & AT91_SSC_CKS);
-
-               tfmr =    (( AT91_SSC_FSEDGE_POSITIVE        ) & AT91_SSC_FSEDGE)
-                       | (( 0                          << 23) & AT91_SSC_FSDEN)
-                       | (( AT91_SSC_FSOS_POSITIVE          ) & AT91_SSC_FSOS)
-                       | (( 0                          << 16) & AT91_SSC_FSLEN)
-                       | (((channels - 1)              <<  8) & AT91_SSC_DATNB)
-                       | (( 1                          <<  7) & AT91_SSC_MSBF)
-                       | (( 0                          <<  5) & AT91_SSC_DATDEF)
-                       | (((bits - 1)                  <<  0) & AT91_SSC_DATALEN);
-
-
-
-                       break;
-
-       case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
-       default:
-               printk(KERN_WARNING "at91-ssc: unsupported DAI format 0x%x.\n",
-                       ssc_p->daifmt);
-               return -EINVAL;
-               break;
-       }
-       DBG("RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n", rcmr, rfmr, tcmr, tfmr);
-
-       if (!ssc_p->initialized) {
-
-               /* Enable PMC peripheral clock for this SSC */
-               DBG("Starting pid %d clock\n", ssc_p->ssc.pid);
-               at91_sys_write(AT91_PMC_PCER, 1<<ssc_p->ssc.pid);
-
-               /* Reset the SSC and its PDC registers */
-               at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST);
-
-               at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RPR, 0);
-               at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RCR, 0);
-               at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RNPR, 0);
-               at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RNCR, 0);
-               at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TPR, 0);
-               at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TCR, 0);
-               at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TNPR, 0);
-               at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TNCR, 0);
-
-               if ((ret = request_irq(ssc_p->ssc.pid, at91_ssc_interrupt,
-                                       0, ssc_p->name, ssc_p)) < 0) {
-                       printk(KERN_WARNING "at91-ssc: request_irq failure\n");
-
-                       DBG("Stopping pid %d clock\n", ssc_p->ssc.pid);
-                       at91_sys_write(AT91_PMC_PCDR, 1<<ssc_p->ssc.pid);
-                       return ret;
-               }
-
-               ssc_p->initialized = 1;
-       }
-
-       /* set SSC clock mode register */
-       at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->cmr_div);
-
-       /* set receive clock mode and format */
-       at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, rcmr);
-       at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, rfmr);
-
-       /* set transmit clock mode and format */
-       at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, tcmr);
-       at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, tfmr);
-
-       DBG("hw_params: SSC initialized\n");
-       return 0;
-}
-
-
-static int at91_ssc_prepare(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
-       struct at91_pcm_dma_params *dma_params;
-       int dir;
-
-       dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
-       dma_params = ssc_p->dma_params[dir];
-
-       at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR,
-                       dma_params->mask->ssc_enable);
-
-       DBG("%s enabled SSC_SR=0x%08lx\n", dir ? "receive" : "transmit",
-               at91_ssc_read(dma_params->ssc_base + AT91_SSC_SR));
-       return 0;
-}
-
-
-#ifdef CONFIG_PM
-static int at91_ssc_suspend(struct platform_device *pdev,
-       struct snd_soc_dai *cpu_dai)
-{
-       struct at91_ssc_info *ssc_p;
-
-       if(!cpu_dai->active)
-               return 0;
-
-       ssc_p = &ssc_info[cpu_dai->id];
-
-       /* Save the status register before disabling transmit and receive. */
-       ssc_p->ssc_state.ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR);
-       at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR,
-                       AT91_SSC_TXDIS | AT91_SSC_RXDIS);
-
-       /* Save the current interrupt mask, then disable unmasked interrupts. */
-       ssc_p->ssc_state.ssc_imr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR);
-       at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IDR, ssc_p->ssc_state.ssc_imr);
-
-       ssc_p->ssc_state.ssc_cmr  = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_CMR);
-       ssc_p->ssc_state.ssc_rcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR);
-       ssc_p->ssc_state.ssc_rfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RFMR);
-       ssc_p->ssc_state.ssc_tcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_TCMR);
-       ssc_p->ssc_state.ssc_tfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_TFMR);
-
-       return 0;
-}
-
-static int at91_ssc_resume(struct platform_device *pdev,
-       struct snd_soc_dai *cpu_dai)
-{
-       struct at91_ssc_info *ssc_p;
-
-       if(!cpu_dai->active)
-               return 0;
-
-       ssc_p = &ssc_info[cpu_dai->id];
-
-       at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, ssc_p->ssc_state.ssc_tfmr);
-       at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, ssc_p->ssc_state.ssc_tcmr);
-       at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, ssc_p->ssc_state.ssc_rfmr);
-       at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->ssc_state.ssc_rcmr);
-       at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR,  ssc_p->ssc_state.ssc_cmr);
-
-       at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IER,  ssc_p->ssc_state.ssc_imr);
-
-       at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR,
-               ((ssc_p->ssc_state.ssc_sr & AT91_SSC_RXENA) ? AT91_SSC_RXEN : 0) |
-               ((ssc_p->ssc_state.ssc_sr & AT91_SSC_TXENA) ? AT91_SSC_TXEN : 0));
-
-       return 0;
-}
-
-#else
-#define at91_ssc_suspend       NULL
-#define at91_ssc_resume                NULL
-#endif
-
-#define AT91_SSC_RATES (SNDRV_PCM_RATE_8000  | SNDRV_PCM_RATE_11025 |\
-                       SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
-                       SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
-                       SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
-                       SNDRV_PCM_RATE_96000)
-
-#define AT91_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_LE |\
-                         SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
-
-struct snd_soc_dai at91_ssc_dai[NUM_SSC_DEVICES] = {
-       {       .name = "at91-ssc0",
-               .id = 0,
-               .type = SND_SOC_DAI_PCM,
-               .suspend = at91_ssc_suspend,
-               .resume = at91_ssc_resume,
-               .playback = {
-                       .channels_min = 1,
-                       .channels_max = 2,
-                       .rates = AT91_SSC_RATES,
-                       .formats = AT91_SSC_FORMATS,},
-               .capture = {
-                       .channels_min = 1,
-                       .channels_max = 2,
-                       .rates = AT91_SSC_RATES,
-                       .formats = AT91_SSC_FORMATS,},
-               .ops = {
-                       .startup = at91_ssc_startup,
-                       .shutdown = at91_ssc_shutdown,
-                       .prepare = at91_ssc_prepare,
-                       .hw_params = at91_ssc_hw_params,},
-               .dai_ops = {
-                       .set_sysclk = at91_ssc_set_dai_sysclk,
-                       .set_fmt = at91_ssc_set_dai_fmt,
-                       .set_clkdiv = at91_ssc_set_dai_clkdiv,},
-               .private_data = &ssc_info[0].ssc,
-       },
-#if NUM_SSC_DEVICES == 3
-       {       .name = "at91-ssc1",
-               .id = 1,
-               .type = SND_SOC_DAI_PCM,
-               .suspend = at91_ssc_suspend,
-               .resume = at91_ssc_resume,
-               .playback = {
-                       .channels_min = 1,
-                       .channels_max = 2,
-                       .rates = AT91_SSC_RATES,
-                       .formats = AT91_SSC_FORMATS,},
-               .capture = {
-                       .channels_min = 1,
-                       .channels_max = 2,
-                       .rates = AT91_SSC_RATES,
-                       .formats = AT91_SSC_FORMATS,},
-               .ops = {
-                       .startup = at91_ssc_startup,
-                       .shutdown = at91_ssc_shutdown,
-                       .prepare = at91_ssc_prepare,
-                       .hw_params = at91_ssc_hw_params,},
-               .dai_ops = {
-                       .set_sysclk = at91_ssc_set_dai_sysclk,
-                       .set_fmt = at91_ssc_set_dai_fmt,
-                       .set_clkdiv = at91_ssc_set_dai_clkdiv,},
-               .private_data = &ssc_info[1].ssc,
-       },
-       {       .name = "at91-ssc2",
-               .id = 2,
-               .type = SND_SOC_DAI_PCM,
-               .suspend = at91_ssc_suspend,
-               .resume = at91_ssc_resume,
-               .playback = {
-                       .channels_min = 1,
-                       .channels_max = 2,
-                       .rates = AT91_SSC_RATES,
-                       .formats = AT91_SSC_FORMATS,},
-               .capture = {
-                       .channels_min = 1,
-                       .channels_max = 2,
-                       .rates = AT91_SSC_RATES,
-                       .formats = AT91_SSC_FORMATS,},
-               .ops = {
-                       .startup = at91_ssc_startup,
-                       .shutdown = at91_ssc_shutdown,
-                       .prepare = at91_ssc_prepare,
-                       .hw_params = at91_ssc_hw_params,},
-               .dai_ops = {
-                       .set_sysclk = at91_ssc_set_dai_sysclk,
-                       .set_fmt = at91_ssc_set_dai_fmt,
-                       .set_clkdiv = at91_ssc_set_dai_clkdiv,},
-               .private_data = &ssc_info[2].ssc,
-       },
-#endif
-};
-
-EXPORT_SYMBOL_GPL(at91_ssc_dai);
-
-/* Module information */
-MODULE_AUTHOR("Frank Mandarino, fmandarino@endrelia.com, www.endrelia.com");
-MODULE_DESCRIPTION("AT91 SSC ASoC Interface");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/at91/at91-ssc.h b/sound/soc/at91/at91-ssc.h
deleted file mode 100644 (file)
index 6b7bf38..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * at91-ssc.h - ALSA SSC interface for the Atmel AT91 SoC
- *
- * Author:     Frank Mandarino <fmandarino@endrelia.com>
- *             Endrelia Technologies Inc.
- * Created:    Jan 9, 2007
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef _AT91_SSC_H
-#define _AT91_SSC_H
-
-/* SSC system clock ids */
-#define AT91_SYSCLK_MCK                0 /* SSC uses AT91 MCK as system clock */
-
-/* SSC divider ids */
-#define AT91SSC_CMR_DIV                0 /* MCK divider for BCLK */
-#define AT91SSC_TCMR_PERIOD    1 /* BCLK divider for transmit FS */
-#define AT91SSC_RCMR_PERIOD    2 /* BCLK divider for receive FS */
-
-extern struct snd_soc_dai at91_ssc_dai[];
-
-#endif /* _AT91_SSC_H */
-
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
new file mode 100644 (file)
index 0000000..a608d70
--- /dev/null
@@ -0,0 +1,43 @@
+config SND_ATMEL_SOC
+       tristate "SoC Audio for the Atmel System-on-Chip"
+       depends on ARCH_AT91 || AVR32
+       help
+         Say Y or M if you want to add support for codecs attached to
+         the ATMEL SSC interface. You will also need
+         to select the audio interfaces to support below.
+
+config SND_ATMEL_SOC_SSC
+       tristate
+       depends on SND_ATMEL_SOC
+       help
+         Say Y or M if you want to add support for codecs the
+         ATMEL SSC interface. You will also needs to select the individual
+         machine drivers to support below.
+
+config SND_AT91_SOC_SAM9G20_WM8731
+       tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
+       depends on ATMEL_SSC && ARCH_AT91SAM9G20 && SND_ATMEL_SOC
+       select SND_ATMEL_SOC_SSC
+       select SND_SOC_WM8731
+       help
+         Say Y if you want to add support for SoC audio on WM8731-based
+         AT91sam9g20 evaluation board.
+
+config SND_AT32_SOC_PLAYPAQ
+        tristate "SoC Audio support for PlayPaq with WM8510"
+        depends on SND_ATMEL_SOC && BOARD_PLAYPAQ
+        select SND_ATMEL_SOC_SSC
+        select SND_SOC_WM8510
+        help
+          Say Y or M here if you want to add support for SoC audio
+          on the LRS PlayPaq.
+
+config SND_AT32_SOC_PLAYPAQ_SLAVE
+        bool "Run CODEC on PlayPaq in slave mode"
+        depends on SND_AT32_SOC_PLAYPAQ
+        default n
+        help
+          Say Y if you want to run with the AT32 SSC generating the BCLK
+          and FRAME signals on the PlayPaq.  Unless you want to play
+          with the AT32 as the SSC master, you probably want to say N here,
+          as this will give you better sound quality.
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
new file mode 100644 (file)
index 0000000..f54a7cc
--- /dev/null
@@ -0,0 +1,15 @@
+# AT91 Platform Support
+snd-soc-atmel-pcm-objs := atmel-pcm.o
+snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
+
+obj-$(CONFIG_SND_ATMEL_SOC) += snd-soc-atmel-pcm.o
+obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
+
+# AT91 Machine Support
+snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
+
+# AT32 Machine Support
+snd-soc-playpaq-objs := playpaq_wm8510.o
+
+obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
+obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o
diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c
new file mode 100644 (file)
index 0000000..1fac5ef
--- /dev/null
@@ -0,0 +1,494 @@
+/*
+ * atmel-pcm.c  --  ALSA PCM interface for the Atmel atmel SoC.
+ *
+ *  Copyright (C) 2005 SAN People
+ *  Copyright (C) 2008 Atmel
+ *
+ * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ *
+ * Based on at91-pcm. by:
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Copyright 2006 Endrelia Technologies Inc.
+ *
+ * Based on pxa2xx-pcm.c by:
+ *
+ * Author:     Nicolas Pitre
+ * Created:    Nov 30, 2004
+ * Copyright:  (C) 2004 MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/atmel_pdc.h>
+#include <linux/atmel-ssc.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <mach/hardware.h>
+
+#include "atmel-pcm.h"
+
+
+/*--------------------------------------------------------------------------*\
+ * Hardware definition
+\*--------------------------------------------------------------------------*/
+/* TODO: These values were taken from the AT91 platform driver, check
+ *      them against real values for AT32
+ */
+static const struct snd_pcm_hardware atmel_pcm_hardware = {
+       .info                   = SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_PAUSE,
+       .formats                = SNDRV_PCM_FMTBIT_S16_LE,
+       .period_bytes_min       = 32,
+       .period_bytes_max       = 8192,
+       .periods_min            = 2,
+       .periods_max            = 1024,
+       .buffer_bytes_max       = 32 * 1024,
+};
+
+
+/*--------------------------------------------------------------------------*\
+ * Data types
+\*--------------------------------------------------------------------------*/
+struct atmel_runtime_data {
+       struct atmel_pcm_dma_params *params;
+       dma_addr_t dma_buffer;          /* physical address of dma buffer */
+       dma_addr_t dma_buffer_end;      /* first address beyond DMA buffer */
+       size_t period_size;
+
+       dma_addr_t period_ptr;          /* physical address of next period */
+       int periods;                    /* period index of period_ptr */
+
+       /* PDC register save */
+       u32 pdc_xpr_save;
+       u32 pdc_xcr_save;
+       u32 pdc_xnpr_save;
+       u32 pdc_xncr_save;
+};
+
+
+/*--------------------------------------------------------------------------*\
+ * Helper functions
+\*--------------------------------------------------------------------------*/
+static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
+       int stream)
+{
+       struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+       size_t size = atmel_pcm_hardware.buffer_bytes_max;
+
+       buf->dev.type = SNDRV_DMA_TYPE_DEV;
+       buf->dev.dev = pcm->card->dev;
+       buf->private_data = NULL;
+       buf->area = dma_alloc_coherent(pcm->card->dev, size,
+                                         &buf->addr, GFP_KERNEL);
+       pr_debug("atmel-pcm:"
+               "preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
+               (void *) buf->area,
+               (void *) buf->addr,
+               size);
+
+       if (!buf->area)
+               return -ENOMEM;
+
+       buf->bytes = size;
+       return 0;
+}
+/*--------------------------------------------------------------------------*\
+ * ISR
+\*--------------------------------------------------------------------------*/
+static void atmel_pcm_dma_irq(u32 ssc_sr,
+       struct snd_pcm_substream *substream)
+{
+       struct atmel_runtime_data *prtd = substream->runtime->private_data;
+       struct atmel_pcm_dma_params *params = prtd->params;
+       static int count;
+
+       count++;
+
+       if (ssc_sr & params->mask->ssc_endbuf) {
+               pr_warning("atmel-pcm: buffer %s on %s"
+                               " (SSC_SR=%#x, count=%d)\n",
+                               substream->stream == SNDRV_PCM_STREAM_PLAYBACK
+                               ? "underrun" : "overrun",
+                               params->name, ssc_sr, count);
+
+               /* re-start the PDC */
+               ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+                          params->mask->pdc_disable);
+               prtd->period_ptr += prtd->period_size;
+               if (prtd->period_ptr >= prtd->dma_buffer_end)
+                       prtd->period_ptr = prtd->dma_buffer;
+
+               ssc_writex(params->ssc->regs, params->pdc->xpr,
+                          prtd->period_ptr);
+               ssc_writex(params->ssc->regs, params->pdc->xcr,
+                          prtd->period_size / params->pdc_xfer_size);
+               ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+                          params->mask->pdc_enable);
+       }
+
+       if (ssc_sr & params->mask->ssc_endx) {
+               /* Load the PDC next pointer and counter registers */
+               prtd->period_ptr += prtd->period_size;
+               if (prtd->period_ptr >= prtd->dma_buffer_end)
+                       prtd->period_ptr = prtd->dma_buffer;
+
+               ssc_writex(params->ssc->regs, params->pdc->xnpr,
+                          prtd->period_ptr);
+               ssc_writex(params->ssc->regs, params->pdc->xncr,
+                          prtd->period_size / params->pdc_xfer_size);
+       }
+
+       snd_pcm_period_elapsed(substream);
+}
+
+
+/*--------------------------------------------------------------------------*\
+ * PCM operations
+\*--------------------------------------------------------------------------*/
+static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct atmel_runtime_data *prtd = runtime->private_data;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+       /* this may get called several times by oss emulation
+        * with different params */
+
+       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+       runtime->dma_bytes = params_buffer_bytes(params);
+
+       prtd->params = rtd->dai->cpu_dai->dma_data;
+       prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
+
+       prtd->dma_buffer = runtime->dma_addr;
+       prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
+       prtd->period_size = params_period_bytes(params);
+
+       pr_debug("atmel-pcm: "
+               "hw_params: DMA for %s initialized "
+               "(dma_bytes=%u, period_size=%u)\n",
+               prtd->params->name,
+               runtime->dma_bytes,
+               prtd->period_size);
+       return 0;
+}
+
+static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       struct atmel_runtime_data *prtd = substream->runtime->private_data;
+       struct atmel_pcm_dma_params *params = prtd->params;
+
+       if (params != NULL) {
+               ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
+                          params->mask->pdc_disable);
+               prtd->params->dma_intr_handler = NULL;
+       }
+
+       return 0;
+}
+
+static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct atmel_runtime_data *prtd = substream->runtime->private_data;
+       struct atmel_pcm_dma_params *params = prtd->params;
+
+       ssc_writex(params->ssc->regs, SSC_IDR,
+                  params->mask->ssc_endx | params->mask->ssc_endbuf);
+       ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+                  params->mask->pdc_disable);
+       return 0;
+}
+
+static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
+       int cmd)
+{
+       struct snd_pcm_runtime *rtd = substream->runtime;
+       struct atmel_runtime_data *prtd = rtd->private_data;
+       struct atmel_pcm_dma_params *params = prtd->params;
+       int ret = 0;
+
+       pr_debug("atmel-pcm:buffer_size = %ld,"
+               "dma_area = %p, dma_bytes = %u\n",
+               rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               prtd->period_ptr = prtd->dma_buffer;
+
+               ssc_writex(params->ssc->regs, params->pdc->xpr,
+                          prtd->period_ptr);
+               ssc_writex(params->ssc->regs, params->pdc->xcr,
+                          prtd->period_size / params->pdc_xfer_size);
+
+               prtd->period_ptr += prtd->period_size;
+               ssc_writex(params->ssc->regs, params->pdc->xnpr,
+                          prtd->period_ptr);
+               ssc_writex(params->ssc->regs, params->pdc->xncr,
+                          prtd->period_size / params->pdc_xfer_size);
+
+               pr_debug("atmel-pcm: trigger: "
+                       "period_ptr=%lx, xpr=%u, "
+                       "xcr=%u, xnpr=%u, xncr=%u\n",
+                       (unsigned long)prtd->period_ptr,
+                       ssc_readx(params->ssc->regs, params->pdc->xpr),
+                       ssc_readx(params->ssc->regs, params->pdc->xcr),
+                       ssc_readx(params->ssc->regs, params->pdc->xnpr),
+                       ssc_readx(params->ssc->regs, params->pdc->xncr));
+
+               ssc_writex(params->ssc->regs, SSC_IER,
+                          params->mask->ssc_endx | params->mask->ssc_endbuf);
+               ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
+                          params->mask->pdc_enable);
+
+               pr_debug("sr=%u imr=%u\n",
+                       ssc_readx(params->ssc->regs, SSC_SR),
+                       ssc_readx(params->ssc->regs, SSC_IER));
+               break;          /* SNDRV_PCM_TRIGGER_START */
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+                          params->mask->pdc_disable);
+               break;
+
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+                          params->mask->pdc_enable);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static snd_pcm_uframes_t atmel_pcm_pointer(
+       struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct atmel_runtime_data *prtd = runtime->private_data;
+       struct atmel_pcm_dma_params *params = prtd->params;
+       dma_addr_t ptr;
+       snd_pcm_uframes_t x;
+
+       ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
+       x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
+
+       if (x == runtime->buffer_size)
+               x = 0;
+
+       return x;
+}
+
+static int atmel_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct atmel_runtime_data *prtd;
+       int ret = 0;
+
+       snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware);
+
+       /* ensure that buffer size is a multiple of period size */
+       ret = snd_pcm_hw_constraint_integer(runtime,
+                                               SNDRV_PCM_HW_PARAM_PERIODS);
+       if (ret < 0)
+               goto out;
+
+       prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL);
+       if (prtd == NULL) {
+               ret = -ENOMEM;
+               goto out;
+       }
+       runtime->private_data = prtd;
+
+ out:
+       return ret;
+}
+
+static int atmel_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct atmel_runtime_data *prtd = substream->runtime->private_data;
+
+       kfree(prtd);
+       return 0;
+}
+
+static int atmel_pcm_mmap(struct snd_pcm_substream *substream,
+       struct vm_area_struct *vma)
+{
+       return remap_pfn_range(vma, vma->vm_start,
+                      substream->dma_buffer.addr >> PAGE_SHIFT,
+                      vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+struct snd_pcm_ops atmel_pcm_ops = {
+       .open           = atmel_pcm_open,
+       .close          = atmel_pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = atmel_pcm_hw_params,
+       .hw_free        = atmel_pcm_hw_free,
+       .prepare        = atmel_pcm_prepare,
+       .trigger        = atmel_pcm_trigger,
+       .pointer        = atmel_pcm_pointer,
+       .mmap           = atmel_pcm_mmap,
+};
+
+
+/*--------------------------------------------------------------------------*\
+ * ASoC platform driver
+\*--------------------------------------------------------------------------*/
+static u64 atmel_pcm_dmamask = 0xffffffff;
+
+static int atmel_pcm_new(struct snd_card *card,
+       struct snd_soc_dai *dai, struct snd_pcm *pcm)
+{
+       int ret = 0;
+
+       if (!card->dev->dma_mask)
+               card->dev->dma_mask = &atmel_pcm_dmamask;
+       if (!card->dev->coherent_dma_mask)
+               card->dev->coherent_dma_mask = 0xffffffff;
+
+       if (dai->playback.channels_min) {
+               ret = atmel_pcm_preallocate_dma_buffer(pcm,
+                       SNDRV_PCM_STREAM_PLAYBACK);
+               if (ret)
+                       goto out;
+       }
+
+       if (dai->capture.channels_min) {
+               pr_debug("at32-pcm:"
+                               "Allocating PCM capture DMA buffer\n");
+               ret = atmel_pcm_preallocate_dma_buffer(pcm,
+                       SNDRV_PCM_STREAM_CAPTURE);
+               if (ret)
+                       goto out;
+       }
+ out:
+       return ret;
+}
+
+static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+       struct snd_pcm_substream *substream;
+       struct snd_dma_buffer *buf;
+       int stream;
+
+       for (stream = 0; stream < 2; stream++) {
+               substream = pcm->streams[stream].substream;
+               if (!substream)
+                       continue;
+
+               buf = &substream->dma_buffer;
+               if (!buf->area)
+                       continue;
+               dma_free_coherent(pcm->card->dev, buf->bytes,
+                                 buf->area, buf->addr);
+               buf->area = NULL;
+       }
+}
+
+#ifdef CONFIG_PM
+static int atmel_pcm_suspend(struct snd_soc_dai *dai)
+{
+       struct snd_pcm_runtime *runtime = dai->runtime;
+       struct atmel_runtime_data *prtd;
+       struct atmel_pcm_dma_params *params;
+
+       if (!runtime)
+               return 0;
+
+       prtd = runtime->private_data;
+       params = prtd->params;
+
+       /* disable the PDC and save the PDC registers */
+
+       ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable);
+
+       prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
+       prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
+       prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
+       prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
+
+       return 0;
+}
+
+static int atmel_pcm_resume(struct snd_soc_dai *dai)
+{
+       struct snd_pcm_runtime *runtime = dai->runtime;
+       struct atmel_runtime_data *prtd;
+       struct atmel_pcm_dma_params *params;
+
+       if (!runtime)
+               return 0;
+
+       prtd = runtime->private_data;
+       params = prtd->params;
+
+       /* restore the PDC registers and enable the PDC */
+       ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
+       ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
+       ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
+       ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
+
+       ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable);
+       return 0;
+}
+#else
+#define atmel_pcm_suspend      NULL
+#define atmel_pcm_resume       NULL
+#endif
+
+struct snd_soc_platform atmel_soc_platform = {
+       .name           = "atmel-audio",
+       .pcm_ops        = &atmel_pcm_ops,
+       .pcm_new        = atmel_pcm_new,
+       .pcm_free       = atmel_pcm_free_dma_buffers,
+       .suspend        = atmel_pcm_suspend,
+       .resume         = atmel_pcm_resume,
+};
+EXPORT_SYMBOL_GPL(atmel_soc_platform);
+
+static int __init atmel_pcm_modinit(void)
+{
+       return snd_soc_register_platform(&atmel_soc_platform);
+}
+module_init(atmel_pcm_modinit);
+
+static void __exit atmel_pcm_modexit(void)
+{
+       snd_soc_unregister_platform(&atmel_soc_platform);
+}
+module_exit(atmel_pcm_modexit);
+
+MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
+MODULE_DESCRIPTION("Atmel PCM module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h
new file mode 100644 (file)
index 0000000..ec9b282
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * at91-pcm.h - ALSA PCM interface for the Atmel AT91 SoC.
+ *
+ *  Copyright (C) 2005 SAN People
+ *  Copyright (C) 2008 Atmel
+ *
+ * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ *
+ * Based on at91-pcm. by:
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Copyright 2006 Endrelia Technologies Inc.
+ *
+ * Based on pxa2xx-pcm.c by:
+ *
+ * Author:     Nicolas Pitre
+ * Created:    Nov 30, 2004
+ * Copyright:  (C) 2004 MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _ATMEL_PCM_H
+#define _ATMEL_PCM_H
+
+#include <linux/atmel-ssc.h>
+
+/*
+ * Registers and status bits that are required by the PCM driver.
+ */
+struct atmel_pdc_regs {
+       unsigned int    xpr;            /* PDC recv/trans pointer */
+       unsigned int    xcr;            /* PDC recv/trans counter */
+       unsigned int    xnpr;           /* PDC next recv/trans pointer */
+       unsigned int    xncr;           /* PDC next recv/trans counter */
+       unsigned int    ptcr;           /* PDC transfer control */
+};
+
+struct atmel_ssc_mask {
+       u32     ssc_enable;             /* SSC recv/trans enable */
+       u32     ssc_disable;            /* SSC recv/trans disable */
+       u32     ssc_endx;               /* SSC ENDTX or ENDRX */
+       u32     ssc_endbuf;             /* SSC TXBUFE or RXBUFF */
+       u32     pdc_enable;             /* PDC recv/trans enable */
+       u32     pdc_disable;            /* PDC recv/trans disable */
+};
+
+/*
+ * This structure, shared between the PCM driver and the interface,
+ * contains all information required by the PCM driver to perform the
+ * PDC DMA operation.  All fields except dma_intr_handler() are initialized
+ * by the interface.  The dms_intr_handler() pointer is set by the PCM
+ * driver and called by the interface SSC interrupt handler if it is
+ * non-NULL.
+ */
+struct atmel_pcm_dma_params {
+       char *name;                     /* stream identifier */
+       int pdc_xfer_size;              /* PDC counter increment in bytes */
+       struct ssc_device *ssc;         /* SSC device for stream */
+       struct atmel_pdc_regs *pdc;     /* PDC receive or transmit registers */
+       struct atmel_ssc_mask *mask;    /* SSC & PDC status bits */
+       struct snd_pcm_substream *substream;
+       void (*dma_intr_handler)(u32, struct snd_pcm_substream *);
+};
+
+extern struct snd_soc_platform atmel_soc_platform;
+
+
+/*
+ * SSC register access (since ssc_writel() / ssc_readl() require literal name)
+ */
+#define ssc_readx(base, reg)            (__raw_readl((base) + (reg)))
+#define ssc_writex(base, reg, value)    __raw_writel((value), (base) + (reg))
+
+#endif /* _ATMEL_PCM_H */
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
new file mode 100644 (file)
index 0000000..c5d6790
--- /dev/null
@@ -0,0 +1,790 @@
+/*
+ * atmel_ssc_dai.c  --  ALSA SoC ATMEL SSC Audio Layer Platform driver
+ *
+ * Copyright (C) 2005 SAN People
+ * Copyright (C) 2008 Atmel
+ *
+ * Author: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ *         ATMEL CORP.
+ *
+ * Based on at91-ssc.c by
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Based on pxa2xx Platform drivers by
+ * Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/atmel_pdc.h>
+
+#include <linux/atmel-ssc.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <mach/hardware.h>
+
+#include "atmel-pcm.h"
+#include "atmel_ssc_dai.h"
+
+
+#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
+#define NUM_SSC_DEVICES                1
+#else
+#define NUM_SSC_DEVICES                3
+#endif
+
+/*
+ * SSC PDC registers required by the PCM DMA engine.
+ */
+static struct atmel_pdc_regs pdc_tx_reg = {
+       .xpr            = ATMEL_PDC_TPR,
+       .xcr            = ATMEL_PDC_TCR,
+       .xnpr           = ATMEL_PDC_TNPR,
+       .xncr           = ATMEL_PDC_TNCR,
+};
+
+static struct atmel_pdc_regs pdc_rx_reg = {
+       .xpr            = ATMEL_PDC_RPR,
+       .xcr            = ATMEL_PDC_RCR,
+       .xnpr           = ATMEL_PDC_RNPR,
+       .xncr           = ATMEL_PDC_RNCR,
+};
+
+/*
+ * SSC & PDC status bits for transmit and receive.
+ */
+static struct atmel_ssc_mask ssc_tx_mask = {
+       .ssc_enable     = SSC_BIT(CR_TXEN),
+       .ssc_disable    = SSC_BIT(CR_TXDIS),
+       .ssc_endx       = SSC_BIT(SR_ENDTX),
+       .ssc_endbuf     = SSC_BIT(SR_TXBUFE),
+       .pdc_enable     = ATMEL_PDC_TXTEN,
+       .pdc_disable    = ATMEL_PDC_TXTDIS,
+};
+
+static struct atmel_ssc_mask ssc_rx_mask = {
+       .ssc_enable     = SSC_BIT(CR_RXEN),
+       .ssc_disable    = SSC_BIT(CR_RXDIS),
+       .ssc_endx       = SSC_BIT(SR_ENDRX),
+       .ssc_endbuf     = SSC_BIT(SR_RXBUFF),
+       .pdc_enable     = ATMEL_PDC_RXTEN,
+       .pdc_disable    = ATMEL_PDC_RXTDIS,
+};
+
+
+/*
+ * DMA parameters.
+ */
+static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
+       {{
+       .name           = "SSC0 PCM out",
+       .pdc            = &pdc_tx_reg,
+       .mask           = &ssc_tx_mask,
+       },
+       {
+       .name           = "SSC0 PCM in",
+       .pdc            = &pdc_rx_reg,
+       .mask           = &ssc_rx_mask,
+       } },
+#if NUM_SSC_DEVICES == 3
+       {{
+       .name           = "SSC1 PCM out",
+       .pdc            = &pdc_tx_reg,
+       .mask           = &ssc_tx_mask,
+       },
+       {
+       .name           = "SSC1 PCM in",
+       .pdc            = &pdc_rx_reg,
+       .mask           = &ssc_rx_mask,
+       } },
+       {{
+       .name           = "SSC2 PCM out",
+       .pdc            = &pdc_tx_reg,
+       .mask           = &ssc_tx_mask,
+       },
+       {
+       .name           = "SSC2 PCM in",
+       .pdc            = &pdc_rx_reg,
+       .mask           = &ssc_rx_mask,
+       } },
+#endif
+};
+
+
+static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
+       {
+       .name           = "ssc0",
+       .lock           = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
+       .dir_mask       = SSC_DIR_MASK_UNUSED,
+       .initialized    = 0,
+       },
+#if NUM_SSC_DEVICES == 3
+       {
+       .name           = "ssc1",
+       .lock           = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
+       .dir_mask       = SSC_DIR_MASK_UNUSED,
+       .initialized    = 0,
+       },
+       {
+       .name           = "ssc2",
+       .lock           = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
+       .dir_mask       = SSC_DIR_MASK_UNUSED,
+       .initialized    = 0,
+       },
+#endif
+};
+
+
+/*
+ * SSC interrupt handler.  Passes PDC interrupts to the DMA
+ * interrupt handler in the PCM driver.
+ */
+static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id)
+{
+       struct atmel_ssc_info *ssc_p = dev_id;
+       struct atmel_pcm_dma_params *dma_params;
+       u32 ssc_sr;
+       u32 ssc_substream_mask;
+       int i;
+
+       ssc_sr = (unsigned long)ssc_readl(ssc_p->ssc->regs, SR)
+                       & (unsigned long)ssc_readl(ssc_p->ssc->regs, IMR);
+
+       /*
+        * Loop through the substreams attached to this SSC.  If
+        * a DMA-related interrupt occurred on that substream, call
+        * the DMA interrupt handler function, if one has been
+        * registered in the dma_params structure by the PCM driver.
+        */
+       for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
+               dma_params = ssc_p->dma_params[i];
+
+               if ((dma_params != NULL) &&
+                       (dma_params->dma_intr_handler != NULL)) {
+                       ssc_substream_mask = (dma_params->mask->ssc_endx |
+                                       dma_params->mask->ssc_endbuf);
+                       if (ssc_sr & ssc_substream_mask) {
+                               dma_params->dma_intr_handler(ssc_sr,
+                                               dma_params->
+                                               substream);
+                       }
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+
+/*-------------------------------------------------------------------------*\
+ * DAI functions
+\*-------------------------------------------------------------------------*/
+/*
+ * Startup.  Only that one substream allowed in each direction.
+ */
+static int atmel_ssc_startup(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+       struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+       int dir_mask;
+
+       pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
+               ssc_readl(ssc_p->ssc->regs, SR));
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dir_mask = SSC_DIR_MASK_PLAYBACK;
+       else
+               dir_mask = SSC_DIR_MASK_CAPTURE;
+
+       spin_lock_irq(&ssc_p->lock);
+       if (ssc_p->dir_mask & dir_mask) {
+               spin_unlock_irq(&ssc_p->lock);
+               return -EBUSY;
+       }
+       ssc_p->dir_mask |= dir_mask;
+       spin_unlock_irq(&ssc_p->lock);
+
+       return 0;
+}
+
+/*
+ * Shutdown.  Clear DMA parameters and shutdown the SSC if there
+ * are no other substreams open.
+ */
+static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+       struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+       struct atmel_pcm_dma_params *dma_params;
+       int dir, dir_mask;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dir = 0;
+       else
+               dir = 1;
+
+       dma_params = ssc_p->dma_params[dir];
+
+       if (dma_params != NULL) {
+               ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable);
+               pr_debug("atmel_ssc_shutdown: %s disabled SSC_SR=0x%08x\n",
+                       (dir ? "receive" : "transmit"),
+                       ssc_readl(ssc_p->ssc->regs, SR));
+
+               dma_params->ssc = NULL;
+               dma_params->substream = NULL;
+               ssc_p->dma_params[dir] = NULL;
+       }
+
+       dir_mask = 1 << dir;
+
+       spin_lock_irq(&ssc_p->lock);
+       ssc_p->dir_mask &= ~dir_mask;
+       if (!ssc_p->dir_mask) {
+               if (ssc_p->initialized) {
+                       /* Shutdown the SSC clock. */
+                       pr_debug("atmel_ssc_dau: Stopping clock\n");
+                       clk_disable(ssc_p->ssc->clk);
+
+                       free_irq(ssc_p->ssc->irq, ssc_p);
+                       ssc_p->initialized = 0;
+               }
+
+               /* Reset the SSC */
+               ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
+               /* Clear the SSC dividers */
+               ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0;
+       }
+       spin_unlock_irq(&ssc_p->lock);
+}
+
+
+/*
+ * Record the DAI format for use in hw_params().
+ */
+static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+               unsigned int fmt)
+{
+       struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+
+       ssc_p->daifmt = fmt;
+       return 0;
+}
+
+/*
+ * Record SSC clock dividers for use in hw_params().
+ */
+static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+       int div_id, int div)
+{
+       struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+
+       switch (div_id) {
+       case ATMEL_SSC_CMR_DIV:
+               /*
+                * The same master clock divider is used for both
+                * transmit and receive, so if a value has already
+                * been set, it must match this value.
+                */
+               if (ssc_p->cmr_div == 0)
+                       ssc_p->cmr_div = div;
+               else
+                       if (div != ssc_p->cmr_div)
+                               return -EBUSY;
+               break;
+
+       case ATMEL_SSC_TCMR_PERIOD:
+               ssc_p->tcmr_period = div;
+               break;
+
+       case ATMEL_SSC_RCMR_PERIOD:
+               ssc_p->rcmr_period = div;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * Configure the SSC.
+ */
+static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params,
+       struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+       int id = rtd->dai->cpu_dai->id;
+       struct atmel_ssc_info *ssc_p = &ssc_info[id];
+       struct atmel_pcm_dma_params *dma_params;
+       int dir, channels, bits;
+       u32 tfmr, rfmr, tcmr, rcmr;
+       int start_event;
+       int ret;
+
+       /*
+        * Currently, there is only one set of dma params for
+        * each direction.  If more are added, this code will
+        * have to be changed to select the proper set.
+        */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dir = 0;
+       else
+               dir = 1;
+
+       dma_params = &ssc_dma_params[id][dir];
+       dma_params->ssc = ssc_p->ssc;
+       dma_params->substream = substream;
+
+       ssc_p->dma_params[dir] = dma_params;
+
+       /*
+        * The cpu_dai->dma_data field is only used to communicate the
+        * appropriate DMA parameters to the pcm driver hw_params()
+        * function.  It should not be used for other purposes
+        * as it is common to all substreams.
+        */
+       rtd->dai->cpu_dai->dma_data = dma_params;
+
+       channels = params_channels(params);
+
+       /*
+        * Determine sample size in bits and the PDC increment.
+        */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S8:
+               bits = 8;
+               dma_params->pdc_xfer_size = 1;
+               break;
+       case SNDRV_PCM_FORMAT_S16_LE:
+               bits = 16;
+               dma_params->pdc_xfer_size = 2;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               bits = 24;
+               dma_params->pdc_xfer_size = 4;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               bits = 32;
+               dma_params->pdc_xfer_size = 4;
+               break;
+       default:
+               printk(KERN_WARNING "atmel_ssc_dai: unsupported PCM format");
+               return -EINVAL;
+       }
+
+       /*
+        * The SSC only supports up to 16-bit samples in I2S format, due
+        * to the size of the Frame Mode Register FSLEN field.
+        */
+       if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S
+               && bits > 16) {
+               printk(KERN_WARNING
+                               "atmel_ssc_dai: sample size %d"
+                               "is too large for I2S\n", bits);
+               return -EINVAL;
+       }
+
+       /*
+        * Compute SSC register settings.
+        */
+       switch (ssc_p->daifmt
+               & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
+
+       case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+               /*
+                * I2S format, SSC provides BCLK and LRC clocks.
+                *
+                * The SSC transmit and receive clocks are generated
+                * from the MCK divider, and the BCLK signal
+                * is output on the SSC TK line.
+                */
+               rcmr =    SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
+                       | SSC_BF(RCMR_STTDLY, START_DELAY)
+                       | SSC_BF(RCMR_START, SSC_START_FALLING_RF)
+                       | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
+                       | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
+                       | SSC_BF(RCMR_CKS, SSC_CKS_DIV);
+
+               rfmr =    SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+                       | SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
+                       | SSC_BF(RFMR_FSLEN, (bits - 1))
+                       | SSC_BF(RFMR_DATNB, (channels - 1))
+                       | SSC_BIT(RFMR_MSBF)
+                       | SSC_BF(RFMR_LOOP, 0)
+                       | SSC_BF(RFMR_DATLEN, (bits - 1));
+
+               tcmr =    SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
+                       | SSC_BF(TCMR_STTDLY, START_DELAY)
+                       | SSC_BF(TCMR_START, SSC_START_FALLING_RF)
+                       | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
+                       | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
+                       | SSC_BF(TCMR_CKS, SSC_CKS_DIV);
+
+               tfmr =    SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+                       | SSC_BF(TFMR_FSDEN, 0)
+                       | SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
+                       | SSC_BF(TFMR_FSLEN, (bits - 1))
+                       | SSC_BF(TFMR_DATNB, (channels - 1))
+                       | SSC_BIT(TFMR_MSBF)
+                       | SSC_BF(TFMR_DATDEF, 0)
+                       | SSC_BF(TFMR_DATLEN, (bits - 1));
+               break;
+
+       case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+               /*
+                * I2S format, CODEC supplies BCLK and LRC clocks.
+                *
+                * The SSC transmit clock is obtained from the BCLK signal on
+                * on the TK line, and the SSC receive clock is
+                * generated from the transmit clock.
+                *
+                *  For single channel data, one sample is transferred
+                * on the falling edge of the LRC clock.
+                * For two channel data, one sample is
+                * transferred on both edges of the LRC clock.
+                */
+               start_event = ((channels == 1)
+                               ? SSC_START_FALLING_RF
+                               : SSC_START_EDGE_RF);
+
+               rcmr =    SSC_BF(RCMR_PERIOD, 0)
+                       | SSC_BF(RCMR_STTDLY, START_DELAY)
+                       | SSC_BF(RCMR_START, start_event)
+                       | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
+                       | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
+                       | SSC_BF(RCMR_CKS, SSC_CKS_CLOCK);
+
+               rfmr =    SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+                       | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
+                       | SSC_BF(RFMR_FSLEN, 0)
+                       | SSC_BF(RFMR_DATNB, 0)
+                       | SSC_BIT(RFMR_MSBF)
+                       | SSC_BF(RFMR_LOOP, 0)
+                       | SSC_BF(RFMR_DATLEN, (bits - 1));
+
+               tcmr =    SSC_BF(TCMR_PERIOD, 0)
+                       | SSC_BF(TCMR_STTDLY, START_DELAY)
+                       | SSC_BF(TCMR_START, start_event)
+                       | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
+                       | SSC_BF(TCMR_CKO, SSC_CKO_NONE)
+                       | SSC_BF(TCMR_CKS, SSC_CKS_PIN);
+
+               tfmr =    SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+                       | SSC_BF(TFMR_FSDEN, 0)
+                       | SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
+                       | SSC_BF(TFMR_FSLEN, 0)
+                       | SSC_BF(TFMR_DATNB, 0)
+                       | SSC_BIT(TFMR_MSBF)
+                       | SSC_BF(TFMR_DATDEF, 0)
+                       | SSC_BF(TFMR_DATLEN, (bits - 1));
+               break;
+
+       case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
+               /*
+                * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
+                *
+                * The SSC transmit and receive clocks are generated from the
+                * MCK divider, and the BCLK signal is output
+                * on the SSC TK line.
+                */
+               rcmr =    SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
+                       | SSC_BF(RCMR_STTDLY, 1)
+                       | SSC_BF(RCMR_START, SSC_START_RISING_RF)
+                       | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
+                       | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
+                       | SSC_BF(RCMR_CKS, SSC_CKS_DIV);
+
+               rfmr =    SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+                       | SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE)
+                       | SSC_BF(RFMR_FSLEN, 0)
+                       | SSC_BF(RFMR_DATNB, (channels - 1))
+                       | SSC_BIT(RFMR_MSBF)
+                       | SSC_BF(RFMR_LOOP, 0)
+                       | SSC_BF(RFMR_DATLEN, (bits - 1));
+
+               tcmr =    SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
+                       | SSC_BF(TCMR_STTDLY, 1)
+                       | SSC_BF(TCMR_START, SSC_START_RISING_RF)
+                       | SSC_BF(TCMR_CKI, SSC_CKI_RISING)
+                       | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
+                       | SSC_BF(TCMR_CKS, SSC_CKS_DIV);
+
+               tfmr =    SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+                       | SSC_BF(TFMR_FSDEN, 0)
+                       | SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE)
+                       | SSC_BF(TFMR_FSLEN, 0)
+                       | SSC_BF(TFMR_DATNB, (channels - 1))
+                       | SSC_BIT(TFMR_MSBF)
+                       | SSC_BF(TFMR_DATDEF, 0)
+                       | SSC_BF(TFMR_DATLEN, (bits - 1));
+               break;
+
+       case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+       default:
+               printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n",
+                       ssc_p->daifmt);
+               return -EINVAL;
+               break;
+       }
+       pr_debug("atmel_ssc_hw_params: "
+                       "RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
+                       rcmr, rfmr, tcmr, tfmr);
+
+       if (!ssc_p->initialized) {
+
+               /* Enable PMC peripheral clock for this SSC */
+               pr_debug("atmel_ssc_dai: Starting clock\n");
+               clk_enable(ssc_p->ssc->clk);
+
+               /* Reset the SSC and its PDC registers */
+               ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
+
+               ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0);
+               ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0);
+               ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0);
+               ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0);
+
+               ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0);
+               ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0);
+               ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0);
+               ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0);
+
+               ret = request_irq(ssc_p->ssc->irq, atmel_ssc_interrupt, 0,
+                               ssc_p->name, ssc_p);
+               if (ret < 0) {
+                       printk(KERN_WARNING
+                                       "atmel_ssc_dai: request_irq failure\n");
+                       pr_debug("Atmel_ssc_dai: Stoping clock\n");
+                       clk_disable(ssc_p->ssc->clk);
+                       return ret;
+               }
+
+               ssc_p->initialized = 1;
+       }
+
+       /* set SSC clock mode register */
+       ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div);
+
+       /* set receive clock mode and format */
+       ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
+       ssc_writel(ssc_p->ssc->regs, RFMR, rfmr);
+
+       /* set transmit clock mode and format */
+       ssc_writel(ssc_p->ssc->regs, TCMR, tcmr);
+       ssc_writel(ssc_p->ssc->regs, TFMR, tfmr);
+
+       pr_debug("atmel_ssc_dai,hw_params: SSC initialized\n");
+       return 0;
+}
+
+
+static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+       struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+       struct atmel_pcm_dma_params *dma_params;
+       int dir;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dir = 0;
+       else
+               dir = 1;
+
+       dma_params = ssc_p->dma_params[dir];
+
+       ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable);
+
+       pr_debug("%s enabled SSC_SR=0x%08x\n",
+                       dir ? "receive" : "transmit",
+                       ssc_readl(ssc_p->ssc->regs, SR));
+       return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
+{
+       struct atmel_ssc_info *ssc_p;
+
+       if (!cpu_dai->active)
+               return 0;
+
+       ssc_p = &ssc_info[cpu_dai->id];
+
+       /* Save the status register before disabling transmit and receive */
+       ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
+       ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS));
+
+       /* Save the current interrupt mask, then disable unmasked interrupts */
+       ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR);
+       ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr);
+
+       ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR);
+       ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR);
+       ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR);
+       ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR);
+       ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR);
+
+       return 0;
+}
+
+
+
+static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
+{
+       struct atmel_ssc_info *ssc_p;
+       u32 cr;
+
+       if (!cpu_dai->active)
+               return 0;
+
+       ssc_p = &ssc_info[cpu_dai->id];
+
+       /* restore SSC register settings */
+       ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
+       ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr);
+       ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr);
+       ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr);
+       ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr);
+
+       /* re-enable interrupts */
+       ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr);
+
+       /* Re-enable recieve and transmit as appropriate */
+       cr = 0;
+       cr |=
+           (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0;
+       cr |=
+           (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0;
+       ssc_writel(ssc_p->ssc->regs, CR, cr);
+
+       return 0;
+}
+#else /* CONFIG_PM */
+#  define atmel_ssc_suspend    NULL
+#  define atmel_ssc_resume     NULL
+#endif /* CONFIG_PM */
+
+
+#define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
+
+#define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_LE |\
+                         SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = {
+       {       .name = "atmel-ssc0",
+               .id = 0,
+               .suspend = atmel_ssc_suspend,
+               .resume = atmel_ssc_resume,
+               .playback = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = ATMEL_SSC_RATES,
+                       .formats = ATMEL_SSC_FORMATS,},
+               .capture = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = ATMEL_SSC_RATES,
+                       .formats = ATMEL_SSC_FORMATS,},
+               .ops = {
+                       .startup = atmel_ssc_startup,
+                       .shutdown = atmel_ssc_shutdown,
+                       .prepare = atmel_ssc_prepare,
+                       .hw_params = atmel_ssc_hw_params,
+                       .set_fmt = atmel_ssc_set_dai_fmt,
+                       .set_clkdiv = atmel_ssc_set_dai_clkdiv,},
+               .private_data = &ssc_info[0],
+       },
+#if NUM_SSC_DEVICES == 3
+       {       .name = "atmel-ssc1",
+               .id = 1,
+               .suspend = atmel_ssc_suspend,
+               .resume = atmel_ssc_resume,
+               .playback = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = ATMEL_SSC_RATES,
+                       .formats = ATMEL_SSC_FORMATS,},
+               .capture = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = ATMEL_SSC_RATES,
+                       .formats = ATMEL_SSC_FORMATS,},
+               .ops = {
+                       .startup = atmel_ssc_startup,
+                       .shutdown = atmel_ssc_shutdown,
+                       .prepare = atmel_ssc_prepare,
+                       .hw_params = atmel_ssc_hw_params,
+                       .set_fmt = atmel_ssc_set_dai_fmt,
+                       .set_clkdiv = atmel_ssc_set_dai_clkdiv,},
+               .private_data = &ssc_info[1],
+       },
+       {       .name = "atmel-ssc2",
+               .id = 2,
+               .suspend = atmel_ssc_suspend,
+               .resume = atmel_ssc_resume,
+               .playback = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = ATMEL_SSC_RATES,
+                       .formats = ATMEL_SSC_FORMATS,},
+               .capture = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = ATMEL_SSC_RATES,
+                       .formats = ATMEL_SSC_FORMATS,},
+               .ops = {
+                       .startup = atmel_ssc_startup,
+                       .shutdown = atmel_ssc_shutdown,
+                       .prepare = atmel_ssc_prepare,
+                       .hw_params = atmel_ssc_hw_params,
+                       .set_fmt = atmel_ssc_set_dai_fmt,
+                       .set_clkdiv = atmel_ssc_set_dai_clkdiv,},
+               .private_data = &ssc_info[2],
+       },
+#endif
+};
+EXPORT_SYMBOL_GPL(atmel_ssc_dai);
+
+static int __init atmel_ssc_modinit(void)
+{
+       return snd_soc_register_dais(atmel_ssc_dai, ARRAY_SIZE(atmel_ssc_dai));
+}
+module_init(atmel_ssc_modinit);
+
+static void __exit atmel_ssc_modexit(void)
+{
+       snd_soc_unregister_dais(atmel_ssc_dai, ARRAY_SIZE(atmel_ssc_dai));
+}
+module_exit(atmel_ssc_modexit);
+
+/* Module information */
+MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com");
+MODULE_DESCRIPTION("ATMEL SSC ASoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h
new file mode 100644 (file)
index 0000000..a828746
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * atmel_ssc_dai.h - ALSA SSC interface for the Atmel  SoC
+ *
+ * Copyright (C) 2005 SAN People
+ * Copyright (C) 2008 Atmel
+ *
+ * Author: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ *         ATMEL CORP.
+ *
+ * Based on at91-ssc.c by
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Based on pxa2xx Platform drivers by
+ * Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _ATMEL_SSC_DAI_H
+#define _ATMEL_SSC_DAI_H
+
+#include <linux/types.h>
+#include <linux/atmel-ssc.h>
+
+#include "atmel-pcm.h"
+
+/* SSC system clock ids */
+#define ATMEL_SYSCLK_MCK       0 /* SSC uses AT91 MCK as system clock */
+
+/* SSC divider ids */
+#define ATMEL_SSC_CMR_DIV      0 /* MCK divider for BCLK */
+#define ATMEL_SSC_TCMR_PERIOD  1 /* BCLK divider for transmit FS */
+#define ATMEL_SSC_RCMR_PERIOD  2 /* BCLK divider for receive FS */
+/*
+ * SSC direction masks
+ */
+#define SSC_DIR_MASK_UNUSED    0
+#define SSC_DIR_MASK_PLAYBACK  1
+#define SSC_DIR_MASK_CAPTURE   2
+
+/*
+ * SSC register values that Atmel left out of <linux/atmel-ssc.h>.  These
+ * are expected to be used with SSC_BF
+ */
+/* START bit field values */
+#define SSC_START_CONTINUOUS   0
+#define SSC_START_TX_RX                1
+#define SSC_START_LOW_RF       2
+#define SSC_START_HIGH_RF      3
+#define SSC_START_FALLING_RF   4
+#define SSC_START_RISING_RF    5
+#define SSC_START_LEVEL_RF     6
+#define SSC_START_EDGE_RF      7
+#define SSS_START_COMPARE_0    8
+
+/* CKI bit field values */
+#define SSC_CKI_FALLING                0
+#define SSC_CKI_RISING         1
+
+/* CKO bit field values */
+#define SSC_CKO_NONE           0
+#define SSC_CKO_CONTINUOUS     1
+#define SSC_CKO_TRANSFER       2
+
+/* CKS bit field values */
+#define SSC_CKS_DIV            0
+#define SSC_CKS_CLOCK          1
+#define SSC_CKS_PIN            2
+
+/* FSEDGE bit field values */
+#define SSC_FSEDGE_POSITIVE    0
+#define SSC_FSEDGE_NEGATIVE    1
+
+/* FSOS bit field values */
+#define SSC_FSOS_NONE          0
+#define SSC_FSOS_NEGATIVE      1
+#define SSC_FSOS_POSITIVE      2
+#define SSC_FSOS_LOW           3
+#define SSC_FSOS_HIGH          4
+#define SSC_FSOS_TOGGLE                5
+
+#define START_DELAY            1
+
+struct atmel_ssc_state {
+       u32 ssc_cmr;
+       u32 ssc_rcmr;
+       u32 ssc_rfmr;
+       u32 ssc_tcmr;
+       u32 ssc_tfmr;
+       u32 ssc_sr;
+       u32 ssc_imr;
+};
+
+
+struct atmel_ssc_info {
+       char *name;
+       struct ssc_device *ssc;
+       spinlock_t lock;        /* lock for dir_mask */
+       unsigned short dir_mask;        /* 0=unused, 1=playback, 2=capture */
+       unsigned short initialized;     /* true if SSC has been initialized */
+       unsigned short daifmt;
+       unsigned short cmr_div;
+       unsigned short tcmr_period;
+       unsigned short rcmr_period;
+       struct atmel_pcm_dma_params *dma_params[2];
+       struct atmel_ssc_state ssc_state;
+};
+extern struct snd_soc_dai atmel_ssc_dai[];
+
+#endif /* _AT91_SSC_DAI_H */
similarity index 98%
rename from sound/soc/at32/playpaq_wm8510.c
rename to sound/soc/atmel/playpaq_wm8510.c
index b1966e4..43dd8ce 100644 (file)
@@ -22,7 +22,6 @@
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/clk.h>
@@ -40,8 +39,8 @@
 #include <mach/portmux.h>
 
 #include "../codecs/wm8510.h"
-#include "at32-pcm.h"
-#include "at32-ssc.h"
+#include "atmel-pcm.h"
+#include "atmel_ssc_dai.h"
 
 
 /*-------------------------------------------------------------------------*\
@@ -362,8 +361,9 @@ static struct snd_soc_dai_link playpaq_wm8510_dai = {
 
 
 
-static struct snd_soc_machine snd_soc_machine_playpaq = {
+static struct snd_soc_card snd_soc_playpaq = {
        .name = "LRS_PlayPaq_WM8510",
+       .platform = &at32_soc_platform,
        .dai_link = &playpaq_wm8510_dai,
        .num_links = 1,
 };
@@ -378,8 +378,7 @@ static struct wm8510_setup_data playpaq_wm8510_setup = {
 
 
 static struct snd_soc_device playpaq_wm8510_snd_devdata = {
-       .machine = &snd_soc_machine_playpaq,
-       .platform = &at32_soc_platform,
+       .card = &snd_soc_playpaq,
        .codec_dev = &soc_codec_dev_wm8510,
        .codec_data = &playpaq_wm8510_setup,
 };
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
new file mode 100644 (file)
index 0000000..1fb59a9
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * sam9g20_wm8731  --  SoC audio for AT91SAM9G20-based
+ *                     ATMEL AT91SAM9G20ek board.
+ *
+ *  Copyright (C) 2005 SAN People
+ *  Copyright (C) 2008 Atmel
+ *
+ * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ *
+ * Based on ati_b1_wm8731.c by:
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Copyright 2006 Endrelia Technologies Inc.
+ * Based on corgi.c by:
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <linux/atmel-ssc.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+
+#include "../codecs/wm8731.h"
+#include "atmel-pcm.h"
+#include "atmel_ssc_dai.h"
+
+
+static int at91sam9g20ek_startup(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       int ret;
+
+       /* codec system clock is supplied by PCK0, set to 12MHz */
+       ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
+               12000000, SND_SOC_CLOCK_IN);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static void at91sam9g20ek_shutdown(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+
+       dev_dbg(rtd->socdev->dev, "shutdown");
+}
+
+static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       struct atmel_ssc_info *ssc_p = cpu_dai->private_data;
+       struct ssc_device *ssc = ssc_p->ssc;
+       int ret;
+
+       unsigned int rate;
+       int cmr_div, period;
+
+       if (ssc == NULL) {
+               printk(KERN_INFO "at91sam9g20ek_hw_params: ssc is NULL!\n");
+               return -EINVAL;
+       }
+
+       /* set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+       if (ret < 0)
+               return ret;
+
+       /* set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * The SSC clock dividers depend on the sample rate.  The CMR.DIV
+        * field divides the system master clock MCK to drive the SSC TK
+        * signal which provides the codec BCLK.  The TCMR.PERIOD and
+        * RCMR.PERIOD fields further divide the BCLK signal to drive
+        * the SSC TF and RF signals which provide the codec DACLRC and
+        * ADCLRC clocks.
+        *
+        * The dividers were determined through trial and error, where a
+        * CMR.DIV value is chosen such that the resulting BCLK value is
+        * divisible, or almost divisible, by (2 * sample rate), and then
+        * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1.
+        */
+       rate = params_rate(params);
+
+       switch (rate) {
+       case 8000:
+               cmr_div = 55;   /* BCLK = 133MHz/(2*55) = 1.209MHz */
+               period = 74;    /* LRC = BCLK/(2*(74+1)) ~= 8060,6Hz */
+               break;
+       case 11025:
+               cmr_div = 67;   /* BCLK = 133MHz/(2*60) = 1.108MHz */
+               period = 45;    /* LRC = BCLK/(2*(49+1)) = 11083,3Hz */
+               break;
+       case 16000:
+               cmr_div = 63;   /* BCLK = 133MHz/(2*63) = 1.055MHz */
+               period = 32;    /* LRC = BCLK/(2*(32+1)) = 15993,2Hz */
+               break;
+       case 22050:
+               cmr_div = 52;   /* BCLK = 133MHz/(2*52) = 1.278MHz */
+               period = 28;    /* LRC = BCLK/(2*(28+1)) = 22049Hz */
+               break;
+       case 32000:
+               cmr_div = 66;   /* BCLK = 133MHz/(2*66) = 1.007MHz */
+               period = 15;    /* LRC = BCLK/(2*(15+1)) = 31486,742Hz */
+               break;
+       case 44100:
+               cmr_div = 29;   /* BCLK = 133MHz/(2*29) = 2.293MHz */
+               period = 25;    /* LRC = BCLK/(2*(25+1)) = 44098Hz */
+               break;
+       case 48000:
+               cmr_div = 33;   /* BCLK = 133MHz/(2*33) = 2.015MHz */
+               period = 20;    /* LRC = BCLK/(2*(20+1)) = 47979,79Hz */
+               break;
+       case 88200:
+               cmr_div = 29;   /* BCLK = 133MHz/(2*29) = 2.293MHz */
+               period = 12;    /* LRC = BCLK/(2*(12+1)) = 88196Hz */
+               break;
+       case 96000:
+               cmr_div = 23;   /* BCLK = 133MHz/(2*23) = 2.891MHz */
+               period = 14;    /* LRC = BCLK/(2*(14+1)) = 96376Hz */
+               break;
+       default:
+               printk(KERN_WARNING "unsupported rate %d"
+                               " on at91sam9g20ek board\n", rate);
+               return -EINVAL;
+       }
+
+       /* set the MCK divider for BCLK */
+       ret = snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_CMR_DIV, cmr_div);
+       if (ret < 0)
+               return ret;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               /* set the BCLK divider for DACLRC */
+               ret = snd_soc_dai_set_clkdiv(cpu_dai,
+                                               ATMEL_SSC_TCMR_PERIOD, period);
+       } else {
+               /* set the BCLK divider for ADCLRC */
+               ret = snd_soc_dai_set_clkdiv(cpu_dai,
+                                               ATMEL_SSC_RCMR_PERIOD, period);
+       }
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static struct snd_soc_ops at91sam9g20ek_ops = {
+       .startup = at91sam9g20ek_startup,
+       .hw_params = at91sam9g20ek_hw_params,
+       .shutdown = at91sam9g20ek_shutdown,
+};
+
+
+static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {
+       SND_SOC_DAPM_MIC("Int Mic", NULL),
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+
+       /* speaker connected to LHPOUT */
+       {"Ext Spk", NULL, "LHPOUT"},
+
+       /* mic is connected to Mic Jack, with WM8731 Mic Bias */
+       {"MICIN", NULL, "Mic Bias"},
+       {"Mic Bias", NULL, "Int Mic"},
+};
+
+/*
+ * Logic for a wm8731 as connected on a at91sam9g20ek board.
+ */
+static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
+{
+       printk(KERN_DEBUG
+                       "at91sam9g20ek_wm8731 "
+                       ": at91sam9g20ek_wm8731_init() called\n");
+
+       /* Add specific widgets */
+       snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets,
+                                 ARRAY_SIZE(at91sam9g20ek_dapm_widgets));
+       /* Set up specific audio path interconnects */
+       snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+       /* not connected */
+       snd_soc_dapm_disable_pin(codec, "RLINEIN");
+       snd_soc_dapm_disable_pin(codec, "LLINEIN");
+
+       /* always connected */
+       snd_soc_dapm_enable_pin(codec, "Int Mic");
+       snd_soc_dapm_enable_pin(codec, "Ext Spk");
+
+       snd_soc_dapm_sync(codec);
+
+       return 0;
+}
+
+static struct snd_soc_dai_link at91sam9g20ek_dai = {
+       .name = "WM8731",
+       .stream_name = "WM8731 PCM",
+       .cpu_dai = &atmel_ssc_dai[0],
+       .codec_dai = &wm8731_dai,
+       .init = at91sam9g20ek_wm8731_init,
+       .ops = &at91sam9g20ek_ops,
+};
+
+static struct snd_soc_card snd_soc_at91sam9g20ek = {
+       .name = "WM8731",
+       .platform = &atmel_soc_platform,
+       .dai_link = &at91sam9g20ek_dai,
+       .num_links = 1,
+};
+
+static struct wm8731_setup_data at91sam9g20ek_wm8731_setup = {
+       .i2c_bus = 0,
+       .i2c_address = 0x1b,
+};
+
+static struct snd_soc_device at91sam9g20ek_snd_devdata = {
+       .card = &snd_soc_at91sam9g20ek,
+       .codec_dev = &soc_codec_dev_wm8731,
+       .codec_data = &at91sam9g20ek_wm8731_setup,
+};
+
+static struct platform_device *at91sam9g20ek_snd_device;
+
+static int __init at91sam9g20ek_init(void)
+{
+       struct atmel_ssc_info *ssc_p = at91sam9g20ek_dai.cpu_dai->private_data;
+       struct ssc_device *ssc = NULL;
+       int ret;
+
+       /*
+        * Request SSC device
+        */
+       ssc = ssc_request(0);
+       if (IS_ERR(ssc)) {
+               ret = PTR_ERR(ssc);
+               ssc = NULL;
+               goto err_ssc;
+       }
+       ssc_p->ssc = ssc;
+
+       at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!at91sam9g20ek_snd_device) {
+               printk(KERN_DEBUG
+                               "platform device allocation failed\n");
+               ret = -ENOMEM;
+       }
+
+       platform_set_drvdata(at91sam9g20ek_snd_device,
+                       &at91sam9g20ek_snd_devdata);
+       at91sam9g20ek_snd_devdata.dev = &at91sam9g20ek_snd_device->dev;
+
+       ret = platform_device_add(at91sam9g20ek_snd_device);
+       if (ret) {
+               printk(KERN_DEBUG
+                               "platform device allocation failed\n");
+               platform_device_put(at91sam9g20ek_snd_device);
+       }
+
+       return ret;
+
+err_ssc:
+       return ret;
+}
+
+static void __exit at91sam9g20ek_exit(void)
+{
+       struct atmel_ssc_info *ssc_p = at91sam9g20ek_dai.cpu_dai->private_data;
+       struct ssc_device *ssc;
+
+       if (ssc_p != NULL) {
+               ssc = ssc_p->ssc;
+               if (ssc != NULL)
+                       ssc_free(ssc);
+               ssc_p->ssc = NULL;
+       }
+
+       platform_device_unregister(at91sam9g20ek_snd_device);
+       at91sam9g20ek_snd_device = NULL;
+}
+
+module_init(at91sam9g20ek_init);
+module_exit(at91sam9g20ek_exit);
+
+/* Module information */
+MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
+MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731");
+MODULE_LICENSE("GPL");
index 1466d93..74c823d 100644 (file)
@@ -406,11 +406,12 @@ static int __init au1xpsc_audio_dbdma_init(void)
 {
        au1xpsc_audio_pcmdma[PCM_TX] = NULL;
        au1xpsc_audio_pcmdma[PCM_RX] = NULL;
-       return 0;
+       return snd_soc_register_platform(&au1xpsc_soc_platform);
 }
 
 static void __exit au1xpsc_audio_dbdma_exit(void)
 {
+       snd_soc_unregister_platform(&au1xpsc_soc_platform);
 }
 
 module_init(au1xpsc_audio_dbdma_init);
index 57facba..f0e30ae 100644 (file)
@@ -160,7 +160,8 @@ struct snd_ac97_bus_ops soc_ac97_ops = {
 EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
-                                 struct snd_pcm_hw_params *params)
+                                 struct snd_pcm_hw_params *params,
+                                 struct snd_soc_dai *dai)
 {
        /* FIXME */
        struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
@@ -210,7 +211,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
 }
 
 static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
-                               int cmd)
+                               int cmd, struct snd_soc_dai *dai)
 {
        /* FIXME */
        struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
@@ -313,8 +314,7 @@ static void au1xpsc_ac97_remove(struct platform_device *pdev,
        au1xpsc_ac97_workdata = NULL;
 }
 
-static int au1xpsc_ac97_suspend(struct platform_device *pdev,
-                               struct snd_soc_dai *dai)
+static int au1xpsc_ac97_suspend(struct snd_soc_dai *dai)
 {
        /* save interesting registers and disable PSC */
        au1xpsc_ac97_workdata->pm[0] =
@@ -328,8 +328,7 @@ static int au1xpsc_ac97_suspend(struct platform_device *pdev,
        return 0;
 }
 
-static int au1xpsc_ac97_resume(struct platform_device *pdev,
-                              struct snd_soc_dai *dai)
+static int au1xpsc_ac97_resume(struct snd_soc_dai *dai)
 {
        /* restore PSC clock config */
        au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE,
@@ -345,7 +344,7 @@ static int au1xpsc_ac97_resume(struct platform_device *pdev,
 
 struct snd_soc_dai au1xpsc_ac97_dai = {
        .name                   = "au1xpsc_ac97",
-       .type                   = SND_SOC_DAI_AC97,
+       .ac97_control           = 1,
        .probe                  = au1xpsc_ac97_probe,
        .remove                 = au1xpsc_ac97_remove,
        .suspend                = au1xpsc_ac97_suspend,
@@ -372,11 +371,12 @@ EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
 static int __init au1xpsc_ac97_init(void)
 {
        au1xpsc_ac97_workdata = NULL;
-       return 0;
+       return snd_soc_register_dai(&au1xpsc_ac97_dai);
 }
 
 static void __exit au1xpsc_ac97_exit(void)
 {
+       snd_soc_unregister_dai(&au1xpsc_ac97_dai);
 }
 
 module_init(au1xpsc_ac97_init);
index 9384702..f916de4 100644 (file)
@@ -116,7 +116,8 @@ out:
 }
 
 static int au1xpsc_i2s_hw_params(struct snd_pcm_substream *substream,
-                                struct snd_pcm_hw_params *params)
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
 {
        struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
 
@@ -240,7 +241,8 @@ static int au1xpsc_i2s_stop(struct au1xpsc_audio_data *pscdata, int stype)
        return 0;
 }
 
-static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+                              struct snd_soc_dai *dai)
 {
        struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
        int ret, stype = SUBSTREAM_TYPE(substream);
@@ -337,8 +339,7 @@ static void au1xpsc_i2s_remove(struct platform_device *pdev,
        au1xpsc_i2s_workdata = NULL;
 }
 
-static int au1xpsc_i2s_suspend(struct platform_device *pdev,
-                              struct snd_soc_dai *cpu_dai)
+static int au1xpsc_i2s_suspend(struct snd_soc_dai *cpu_dai)
 {
        /* save interesting register and disable PSC */
        au1xpsc_i2s_workdata->pm[0] =
@@ -352,8 +353,7 @@ static int au1xpsc_i2s_suspend(struct platform_device *pdev,
        return 0;
 }
 
-static int au1xpsc_i2s_resume(struct platform_device *pdev,
-                             struct snd_soc_dai *cpu_dai)
+static int au1xpsc_i2s_resume(struct snd_soc_dai *cpu_dai)
 {
        /* select I2S mode and PSC clock */
        au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
@@ -369,7 +369,6 @@ static int au1xpsc_i2s_resume(struct platform_device *pdev,
 
 struct snd_soc_dai au1xpsc_i2s_dai = {
        .name                   = "au1xpsc_i2s",
-       .type                   = SND_SOC_DAI_I2S,
        .probe                  = au1xpsc_i2s_probe,
        .remove                 = au1xpsc_i2s_remove,
        .suspend                = au1xpsc_i2s_suspend,
@@ -389,8 +388,6 @@ struct snd_soc_dai au1xpsc_i2s_dai = {
        .ops = {
                .trigger        = au1xpsc_i2s_trigger,
                .hw_params      = au1xpsc_i2s_hw_params,
-       },
-       .dai_ops = {
                .set_fmt        = au1xpsc_i2s_set_fmt,
        },
 };
@@ -399,11 +396,12 @@ EXPORT_SYMBOL(au1xpsc_i2s_dai);
 static int __init au1xpsc_i2s_init(void)
 {
        au1xpsc_i2s_workdata = NULL;
-       return 0;
+       return snd_soc_register_dai(&au1xpsc_i2s_dai);
 }
 
 static void __exit au1xpsc_i2s_exit(void)
 {
+       snd_soc_unregister_dai(&au1xpsc_i2s_dai);
 }
 
 module_init(au1xpsc_i2s_init);
index f75ae7f..27683eb 100644 (file)
@@ -42,14 +42,14 @@ static struct snd_soc_dai_link au1xpsc_sample_ac97_dai = {
        .ops            = NULL,
 };
 
-static struct snd_soc_machine au1xpsc_sample_ac97_machine = {
+static struct snd_soc_card au1xpsc_sample_ac97_machine = {
        .name           = "Au1xxx PSC AC97 Audio",
        .dai_link       = &au1xpsc_sample_ac97_dai,
        .num_links      = 1,
 };
 
 static struct snd_soc_device au1xpsc_sample_ac97_devdata = {
-       .machine        = &au1xpsc_sample_ac97_machine,
+       .card           = &au1xpsc_sample_ac97_machine,
        .platform       = &au1xpsc_soc_platform, /* see dbdma2.c */
        .codec_dev      = &soc_codec_dev_ac97,
 };
index dc00620..0a2f8f9 100644 (file)
@@ -1,6 +1,6 @@
 config SND_BF5XX_I2S
        tristate "SoC I2S Audio for the ADI BF5xx chip"
-       depends on BLACKFIN && SND_SOC
+       depends on BLACKFIN
        help
          Say Y or M if you want to add support for codecs attached to
          the Blackfin SPORT (synchronous serial ports) interface in I2S
@@ -13,7 +13,6 @@ config SND_BF5XX_SOC_SSM2602
        select SND_BF5XX_SOC_I2S
        select SND_SOC_SSM2602
        select I2C
-       select I2C_BLACKFIN_TWI
        help
          Say Y if you want to add support for SoC audio on BF527-EZKIT.
 
@@ -35,7 +34,7 @@ config SND_BFIN_AD73311_SE
 
 config SND_BF5XX_AC97
        tristate "SoC AC97 Audio for the ADI BF5xx chip"
-       depends on BLACKFIN && SND_SOC
+       depends on BLACKFIN
        help
          Say Y or M if you want to add support for codecs attached to
          the Blackfin SPORT (synchronous serial ports) interface in slot 16
@@ -47,7 +46,7 @@ config SND_BF5XX_AC97
          properly with this driver. This driver is known to work with the
          Analog Devices line of AC97 codecs.
 
-config SND_MMAP_SUPPORT
+config SND_BF5XX_MMAP_SUPPORT
        bool "Enable MMAP Support"
        depends on SND_BF5XX_AC97
        default y
@@ -55,9 +54,17 @@ config SND_MMAP_SUPPORT
          Say y if you want AC97 driver to support mmap mode.
          We introduce an intermediate buffer to simulate mmap.
 
+config SND_BF5XX_MULTICHAN_SUPPORT
+       bool "Enable Multichannel Support"
+       depends on SND_BF5XX_AC97
+       default n
+       help
+         Say y if you want AC97 driver to support up to 5.1 channel audio.
+         this mode will consume much more memory for DMA.
+
 config SND_BF5XX_SOC_SPORT
        tristate
-       
+
 config SND_BF5XX_SOC_I2S
        tristate
        select SND_BF5XX_SOC_SPORT
@@ -80,7 +87,7 @@ config SND_BF5XX_SPORT_NUM
        int "Set a SPORT for Sound chip"
        depends on (SND_BF5XX_I2S || SND_BF5XX_AC97)
        range 0 3 if BF54x
-       range 0 1 if (BF53x || BF561)
+       range 0 1 if !BF54x
        default 0
        help
          Set the correct SPORT for sound chip.
@@ -90,12 +97,13 @@ config SND_BF5XX_HAVE_COLD_RESET
        depends on SND_BF5XX_AC97
        default y if BFIN548_EZKIT
        default n if !BFIN548_EZKIT
-       
+
 config SND_BF5XX_RESET_GPIO_NUM
        int "Set a GPIO for cold reset"
        depends on SND_BF5XX_HAVE_COLD_RESET
        range 0 159
        default 19 if BFIN548_EZKIT
        default 5 if BFIN537_STAMP
+       default 0
        help
          Set the correct GPIO for RESET the sound chip.
index 25e50d2..8067cfa 100644 (file)
 #include "bf5xx-ac97.h"
 #include "bf5xx-sport.h"
 
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+static unsigned int ac97_chan_mask[] = {
+       SP_FL, /* Mono */
+       SP_STEREO, /* Stereo */
+       SP_2DOT1, /* 2.1*/
+       SP_QUAD,/*Quadraquic*/
+       SP_FL | SP_FR | SP_FC | SP_SL | SP_SR,/*5 channels */
+       SP_5DOT1, /* 5.1 */
+};
+
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
 static void bf5xx_mmap_copy(struct snd_pcm_substream *substream,
         snd_pcm_uframes_t count)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct sport_device *sport = runtime->private_data;
+       unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1];
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               bf5xx_pcm_to_ac97(
-                       (struct ac97_frame *)sport->tx_dma_buf + sport->tx_pos,
-                       (__u32 *)runtime->dma_area + sport->tx_pos, count);
+               bf5xx_pcm_to_ac97((struct ac97_frame *)sport->tx_dma_buf +
+               sport->tx_pos, (__u16 *)runtime->dma_area + sport->tx_pos *
+               runtime->channels, count, chan_mask);
                sport->tx_pos += runtime->period_size;
                if (sport->tx_pos >= runtime->buffer_size)
                        sport->tx_pos %= runtime->buffer_size;
                sport->tx_delay_pos = sport->tx_pos;
        } else {
-               bf5xx_ac97_to_pcm(
-                       (struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos,
-                       (__u32 *)runtime->dma_area + sport->rx_pos, count);
+               bf5xx_ac97_to_pcm((struct ac97_frame *)sport->rx_dma_buf +
+               sport->rx_pos, (__u16 *)runtime->dma_area + sport->rx_pos *
+               runtime->channels, count);
                sport->rx_pos += runtime->period_size;
                if (sport->rx_pos >= runtime->buffer_size)
                        sport->rx_pos %= runtime->buffer_size;
@@ -71,7 +81,7 @@ static void bf5xx_mmap_copy(struct snd_pcm_substream *substream,
 static void bf5xx_dma_irq(void *data)
 {
        struct snd_pcm_substream *pcm = data;
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
        struct snd_pcm_runtime *runtime = pcm->runtime;
        struct sport_device *sport = runtime->private_data;
        bf5xx_mmap_copy(pcm, runtime->period_size);
@@ -90,17 +100,14 @@ static void bf5xx_dma_irq(void *data)
  * The total rx/tx buffer is for ac97 frame to hold all pcm data
  * is  0x20000 * sizeof(struct ac97_frame) / 4.
  */
-#ifdef CONFIG_SND_MMAP_SUPPORT
 static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
        .info                   = SNDRV_PCM_INFO_INTERLEAVED |
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
                                   SNDRV_PCM_INFO_MMAP |
                                   SNDRV_PCM_INFO_MMAP_VALID |
-                                  SNDRV_PCM_INFO_BLOCK_TRANSFER,
-#else
-static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
-       .info                   = SNDRV_PCM_INFO_INTERLEAVED |
-                                 SNDRV_PCM_INFO_BLOCK_TRANSFER,
 #endif
+                                  SNDRV_PCM_INFO_BLOCK_TRANSFER,
+
        .formats                = SNDRV_PCM_FMTBIT_S16_LE,
        .period_bytes_min       = 32,
        .period_bytes_max       = 0x10000,
@@ -123,10 +130,20 @@ static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
 
 static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
 {
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
        struct snd_pcm_runtime *runtime = substream->runtime;
+       struct sport_device *sport = runtime->private_data;
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-       memset(runtime->dma_area, 0, runtime->buffer_size);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               sport->once = 0;
+               if (runtime->dma_area)
+                       memset(runtime->dma_area, 0, runtime->buffer_size);
+               memset(sport->tx_dma_buf, 0, runtime->buffer_size *
+                       sizeof(struct ac97_frame));
+       } else
+               memset(sport->rx_dma_buf, 0, runtime->buffer_size *
+                       sizeof(struct ac97_frame));
+#endif
        snd_pcm_lib_free_pages(substream);
        return 0;
 }
@@ -139,7 +156,7 @@ static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
        /* An intermediate buffer is introduced for implementing mmap for
         * SPORT working in TMD mode(include AC97).
         */
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
                sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods,
@@ -173,24 +190,24 @@ static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
                        bf5xx_mmap_copy(substream, runtime->period_size);
-                       snd_pcm_period_elapsed(substream);
                        sport->tx_delay_pos = 0;
+#endif
                        sport_tx_start(sport);
-               }
-               else
+               } else
                        sport_rx_start(sport);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
                        sport->tx_pos = 0;
 #endif
                        sport_tx_stop(sport);
                } else {
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
                        sport->rx_pos = 0;
 #endif
                        sport_rx_stop(sport);
@@ -208,7 +225,7 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
        struct sport_device *sport = runtime->private_data;
        unsigned int curr;
 
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                curr = sport->tx_delay_pos;
        else
@@ -249,22 +266,7 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
        return ret;
 }
 
-static int bf5xx_pcm_close(struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct sport_device *sport = runtime->private_data;
-
-       pr_debug("%s enter\n", __func__);
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               sport->once = 0;
-               memset(sport->tx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame));
-       } else
-               memset(sport->rx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame));
-
-       return 0;
-}
-
-#ifdef CONFIG_SND_MMAP_SUPPORT
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
 static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
        struct vm_area_struct *vma)
 {
@@ -281,32 +283,29 @@ static    int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
                    void __user *buf, snd_pcm_uframes_t count)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
-
+       unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1];
        pr_debug("%s copy pos:0x%lx count:0x%lx\n",
                        substream->stream ? "Capture" : "Playback", pos, count);
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               bf5xx_pcm_to_ac97(
-                               (struct ac97_frame *)runtime->dma_area + pos,
-                               buf, count);
+               bf5xx_pcm_to_ac97((struct ac97_frame *)runtime->dma_area + pos,
+                       (__u16 *)buf, count, chan_mask);
        else
-               bf5xx_ac97_to_pcm(
-                               (struct ac97_frame *)runtime->dma_area + pos,
-                               buf, count);
+               bf5xx_ac97_to_pcm((struct ac97_frame *)runtime->dma_area + pos,
+                       (__u16 *)buf, count);
        return 0;
 }
 #endif
 
 struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
        .open           = bf5xx_pcm_open,
-       .close          = bf5xx_pcm_close,
        .ioctl          = snd_pcm_lib_ioctl,
        .hw_params      = bf5xx_pcm_hw_params,
        .hw_free        = bf5xx_pcm_hw_free,
        .prepare        = bf5xx_pcm_prepare,
        .trigger        = bf5xx_pcm_trigger,
        .pointer        = bf5xx_pcm_pointer,
-#ifdef CONFIG_SND_MMAP_SUPPORT
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
        .mmap           = bf5xx_pcm_mmap,
 #else
        .copy           = bf5xx_pcm_copy,
@@ -344,7 +343,7 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
  * Need to allocate local buffer when enable
  * MMAP for SPORT working in TMD mode (include AC97).
  */
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                if (!sport_handle->tx_dma_buf) {
                        sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \
@@ -381,7 +380,7 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
        struct snd_pcm_substream *substream;
        struct snd_dma_buffer *buf;
        int stream;
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
        size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
                sizeof(struct ac97_frame) / 4;
 #endif
@@ -395,7 +394,7 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
                        continue;
                dma_free_coherent(NULL, buf->bytes, buf->area, 0);
                buf->area = NULL;
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                if (sport_handle->tx_dma_buf)
                        dma_free_coherent(NULL, size, \
@@ -452,6 +451,18 @@ struct snd_soc_platform bf5xx_ac97_soc_platform = {
 };
 EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform);
 
+static int __init bfin_ac97_init(void)
+{
+       return snd_soc_register_platform(&bf5xx_ac97_soc_platform);
+}
+module_init(bfin_ac97_init);
+
+static void __exit bfin_ac97_exit(void)
+{
+       snd_soc_unregister_platform(&bf5xx_ac97_soc_platform);
+}
+module_exit(bfin_ac97_exit);
+
 MODULE_AUTHOR("Cliff Cai");
 MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module");
 MODULE_LICENSE("GPL");
index 5e5aafb..3be2be6 100644 (file)
 static int *cmd_count;
 static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
 
-#if defined(CONFIG_BF54x)
+static u16 sport_req[][7] = {
+               PIN_REQ_SPORT_0,
+#ifdef PIN_REQ_SPORT_1
+               PIN_REQ_SPORT_1,
+#endif
+#ifdef PIN_REQ_SPORT_2
+               PIN_REQ_SPORT_2,
+#endif
+#ifdef PIN_REQ_SPORT_3
+               PIN_REQ_SPORT_3,
+#endif
+       };
+
 static struct sport_param sport_params[4] = {
        {
                .dma_rx_chan    = CH_SPORT0_RX,
                .dma_tx_chan    = CH_SPORT0_TX,
-               .err_irq        = IRQ_SPORT0_ERR,
+               .err_irq        = IRQ_SPORT0_ERROR,
                .regs           = (struct sport_register *)SPORT0_TCR1,
        },
+#ifdef PIN_REQ_SPORT_1
        {
                .dma_rx_chan    = CH_SPORT1_RX,
                .dma_tx_chan    = CH_SPORT1_TX,
-               .err_irq        = IRQ_SPORT1_ERR,
+               .err_irq        = IRQ_SPORT1_ERROR,
                .regs           = (struct sport_register *)SPORT1_TCR1,
        },
+#endif
+#ifdef PIN_REQ_SPORT_2
        {
                .dma_rx_chan    = CH_SPORT2_RX,
                .dma_tx_chan    = CH_SPORT2_TX,
-               .err_irq        = IRQ_SPORT2_ERR,
+               .err_irq        = IRQ_SPORT2_ERROR,
                .regs           = (struct sport_register *)SPORT2_TCR1,
        },
+#endif
+#ifdef PIN_REQ_SPORT_3
        {
                .dma_rx_chan    = CH_SPORT3_RX,
                .dma_tx_chan    = CH_SPORT3_TX,
-               .err_irq        = IRQ_SPORT3_ERR,
+               .err_irq        = IRQ_SPORT3_ERROR,
                .regs           = (struct sport_register *)SPORT3_TCR1,
        }
-};
-#else
-static struct sport_param sport_params[2] = {
-       {
-               .dma_rx_chan    = CH_SPORT0_RX,
-               .dma_tx_chan    = CH_SPORT0_TX,
-               .err_irq        = IRQ_SPORT0_ERROR,
-               .regs           = (struct sport_register *)SPORT0_TCR1,
-       },
-       {
-               .dma_rx_chan    = CH_SPORT1_RX,
-               .dma_tx_chan    = CH_SPORT1_TX,
-               .err_irq        = IRQ_SPORT1_ERROR,
-               .regs           = (struct sport_register *)SPORT1_TCR1,
-       }
-};
 #endif
+};
 
-void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \
-               size_t count)
+void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src,
+               size_t count, unsigned int chan_mask)
 {
        while (count--) {
-               dst->ac97_tag = TAG_VALID | TAG_PCM;
-               (dst++)->ac97_pcm = *src++;
+               dst->ac97_tag = TAG_VALID;
+               if (chan_mask & SP_FL) {
+                       dst->ac97_pcm_r = *src++;
+                       dst->ac97_tag |= TAG_PCM_RIGHT;
+               }
+               if (chan_mask & SP_FR) {
+                       dst->ac97_pcm_l = *src++;
+                       dst->ac97_tag |= TAG_PCM_LEFT;
+
+               }
+#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
+               if (chan_mask & SP_SR) {
+                       dst->ac97_sl = *src++;
+                       dst->ac97_tag |= TAG_PCM_SL;
+               }
+               if (chan_mask & SP_SL) {
+                       dst->ac97_sr = *src++;
+                       dst->ac97_tag |= TAG_PCM_SR;
+               }
+               if (chan_mask & SP_LFE) {
+                       dst->ac97_lfe = *src++;
+                       dst->ac97_tag |= TAG_PCM_LFE;
+               }
+               if (chan_mask & SP_FC) {
+                       dst->ac97_center = *src++;
+                       dst->ac97_tag |= TAG_PCM_CENTER;
+               }
+#endif
+               dst++;
        }
 }
 EXPORT_SYMBOL(bf5xx_pcm_to_ac97);
 
-void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \
+void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u16 *dst,
                size_t count)
 {
-       while (count--)
-               *(dst++) = (src++)->ac97_pcm;
+       while (count--) {
+               *(dst++) = src->ac97_pcm_l;
+               *(dst++) = src->ac97_pcm_r;
+               src++;
+       }
 }
 EXPORT_SYMBOL(bf5xx_ac97_to_pcm);
 
 static unsigned int sport_tx_curr_frag(struct sport_device *sport)
 {
-       return sport->tx_curr_frag = sport_curr_offset_tx(sport) / \
+       return sport->tx_curr_frag = sport_curr_offset_tx(sport) /
                        sport->tx_fragsize;
 }
 
@@ -130,7 +162,7 @@ static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
 
        sport_incfrag(sport, &nextfrag, 1);
 
-       nextwrite = (struct ac97_frame *)(sport->tx_buf + \
+       nextwrite = (struct ac97_frame *)(sport->tx_buf +
                        nextfrag * sport->tx_fragsize);
        pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n",
                sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]);
@@ -237,8 +269,7 @@ struct snd_ac97_bus_ops soc_ac97_ops = {
 EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 #ifdef CONFIG_PM
-static int bf5xx_ac97_suspend(struct platform_device *pdev,
-       struct snd_soc_dai *dai)
+static int bf5xx_ac97_suspend(struct snd_soc_dai *dai)
 {
        struct sport_device *sport =
                (struct sport_device *)dai->private_data;
@@ -253,8 +284,7 @@ static int bf5xx_ac97_suspend(struct platform_device *pdev,
        return 0;
 }
 
-static int bf5xx_ac97_resume(struct platform_device *pdev,
-       struct snd_soc_dai *dai)
+static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
 {
        int ret;
        struct sport_device *sport =
@@ -297,20 +327,15 @@ static int bf5xx_ac97_resume(struct platform_device *pdev,
 static int bf5xx_ac97_probe(struct platform_device *pdev,
                            struct snd_soc_dai *dai)
 {
-       int ret;
-#if defined(CONFIG_BF54x)
-       u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1,
-                                PIN_REQ_SPORT_2, PIN_REQ_SPORT_3};
-#else
-       u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1};
-#endif
+       int ret = 0;
        cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
        if (cmd_count == NULL)
                return -ENOMEM;
 
        if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
                pr_err("Requesting Peripherals failed\n");
-               return -EFAULT;
+               ret =  -EFAULT;
+               goto peripheral_err;
                }
 
 #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
@@ -318,54 +343,54 @@ static int bf5xx_ac97_probe(struct platform_device *pdev,
        if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) {
                pr_err("Failed to request GPIO_%d for reset\n",
                                CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-               peripheral_free_list(&sport_req[sport_num][0]);
-               return -1;
+               ret =  -1;
+               goto gpio_err;
        }
        gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
 #endif
        sport_handle = sport_init(&sport_params[sport_num], 2, \
                        sizeof(struct ac97_frame), NULL);
        if (!sport_handle) {
-               peripheral_free_list(&sport_req[sport_num][0]);
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
-               gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-#endif
-               return -ENODEV;
+               ret = -ENODEV;
+               goto sport_err;
        }
        /*SPORT works in TDM mode to simulate AC97 transfers*/
        ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
        if (ret) {
                pr_err("SPORT is busy!\n");
-               kfree(sport_handle);
-               peripheral_free_list(&sport_req[sport_num][0]);
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
-               gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-#endif
-               return -EBUSY;
+               ret = -EBUSY;
+               goto sport_config_err;
        }
 
        ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
        if (ret) {
                pr_err("SPORT is busy!\n");
-               kfree(sport_handle);
-               peripheral_free_list(&sport_req[sport_num][0]);
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
-               gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-#endif
-               return -EBUSY;
+               ret = -EBUSY;
+               goto sport_config_err;
        }
 
        ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
        if (ret) {
                pr_err("SPORT is busy!\n");
-               kfree(sport_handle);
-               peripheral_free_list(&sport_req[sport_num][0]);
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
-               gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-#endif
-               return -EBUSY;
+               ret = -EBUSY;
+               goto sport_config_err;
        }
+
        return 0;
+
+sport_config_err:
+       kfree(sport_handle);
+sport_err:
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+       gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
+#endif
+gpio_err:
+       peripheral_free_list(&sport_req[sport_num][0]);
+peripheral_err:
+       free_page((unsigned long)cmd_count);
+       cmd_count = NULL;
+
+       return ret;
 }
 
 static void bf5xx_ac97_remove(struct platform_device *pdev,
@@ -373,6 +398,7 @@ static void bf5xx_ac97_remove(struct platform_device *pdev,
 {
        free_page((unsigned long)cmd_count);
        cmd_count = NULL;
+       peripheral_free_list(&sport_req[sport_num][0]);
 #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
        gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
 #endif
@@ -381,7 +407,7 @@ static void bf5xx_ac97_remove(struct platform_device *pdev,
 struct snd_soc_dai bfin_ac97_dai = {
        .name = "bf5xx-ac97",
        .id = 0,
-       .type = SND_SOC_DAI_AC97,
+       .ac97_control = 1,
        .probe = bf5xx_ac97_probe,
        .remove = bf5xx_ac97_remove,
        .suspend = bf5xx_ac97_suspend,
@@ -389,7 +415,11 @@ struct snd_soc_dai bfin_ac97_dai = {
        .playback = {
                .stream_name = "AC97 Playback",
                .channels_min = 2,
+#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
+               .channels_max = 6,
+#else
                .channels_max = 2,
+#endif
                .rates = SNDRV_PCM_RATE_48000,
                .formats = SNDRV_PCM_FMTBIT_S16_LE, },
        .capture = {
@@ -401,6 +431,18 @@ struct snd_soc_dai bfin_ac97_dai = {
 };
 EXPORT_SYMBOL_GPL(bfin_ac97_dai);
 
+static int __init bfin_ac97_init(void)
+{
+       return snd_soc_register_dai(&bfin_ac97_dai);
+}
+module_init(bfin_ac97_init);
+
+static void __exit bfin_ac97_exit(void)
+{
+       snd_soc_unregister_dai(&bfin_ac97_dai);
+}
+module_exit(bfin_ac97_exit);
+
 MODULE_AUTHOR("Roy Huang");
 MODULE_DESCRIPTION("AC97 driver for ADI Blackfin");
 MODULE_LICENSE("GPL");
index 3f77cc5..3f2a911 100644 (file)
@@ -16,21 +16,46 @@ struct ac97_frame {
        u16 ac97_tag;           /* slot 0 */
        u16 ac97_addr;          /* slot 1 */
        u16 ac97_data;          /* slot 2 */
-       u32 ac97_pcm;           /* slot 3 and 4: left and right pcm data */
+       u16 ac97_pcm_l;         /*slot 3:front left*/
+       u16 ac97_pcm_r;         /*slot 4:front left*/
+#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
+       u16 ac97_mdm_l1;
+       u16 ac97_center;        /*slot 6:center*/
+       u16 ac97_sl;            /*slot 7:surround left*/
+       u16 ac97_sr;            /*slot 8:surround right*/
+       u16 ac97_lfe;           /*slot 9:lfe*/
+#endif
 } __attribute__ ((packed));
 
+/* Speaker location */
+#define SP_FL          0x0001
+#define SP_FR          0x0010
+#define SP_FC          0x0002
+#define SP_LFE         0x0020
+#define SP_SL          0x0004
+#define SP_SR          0x0040
+
+#define SP_STEREO      (SP_FL | SP_FR)
+#define SP_2DOT1       (SP_FL | SP_FR | SP_LFE)
+#define SP_QUAD                (SP_FL | SP_FR | SP_SL | SP_SR)
+#define SP_5DOT1       (SP_FL | SP_FR | SP_FC | SP_LFE | SP_SL | SP_SR)
+
 #define TAG_VALID              0x8000
 #define TAG_CMD                        0x6000
 #define TAG_PCM_LEFT           0x1000
 #define TAG_PCM_RIGHT          0x0800
-#define TAG_PCM                        (TAG_PCM_LEFT | TAG_PCM_RIGHT)
+#define TAG_PCM_MDM_L1         0x0400
+#define TAG_PCM_CENTER         0x0200
+#define TAG_PCM_SL             0x0100
+#define TAG_PCM_SR             0x0080
+#define TAG_PCM_LFE            0x0040
 
 extern struct snd_soc_dai bfin_ac97_dai;
 
-void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \
-               size_t count);
+void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src, \
+               size_t count, unsigned int chan_mask);
 
-void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \
+void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u16 *dst, \
                size_t count);
 
 #endif
index 124425d..d8f5912 100644 (file)
@@ -43,7 +43,7 @@
 #include "bf5xx-ac97-pcm.h"
 #include "bf5xx-ac97.h"
 
-static struct snd_soc_machine bf5xx_board;
+static struct snd_soc_card bf5xx_board;
 
 static int bf5xx_board_startup(struct snd_pcm_substream *substream)
 {
@@ -67,15 +67,15 @@ static struct snd_soc_dai_link bf5xx_board_dai = {
        .ops = &bf5xx_board_ops,
 };
 
-static struct snd_soc_machine bf5xx_board = {
+static struct snd_soc_card bf5xx_board = {
        .name = "bf5xx-board",
+       .platform = &bf5xx_ac97_soc_platform,
        .dai_link = &bf5xx_board_dai,
        .num_links = 1,
 };
 
 static struct snd_soc_device bf5xx_board_snd_devdata = {
-       .machine = &bf5xx_board,
-       .platform = &bf5xx_ac97_soc_platform,
+       .card = &bf5xx_board,
        .codec_dev = &soc_codec_dev_ad1980,
 };
 
index 622c9b9..7f2a5e1 100644 (file)
@@ -65,7 +65,7 @@
 
 #define GPIO_SE CONFIG_SND_BFIN_AD73311_SE
 
-static struct snd_soc_machine bf5xx_ad73311;
+static struct snd_soc_card bf5xx_ad73311;
 
 static int snd_ad73311_startup(void)
 {
@@ -168,7 +168,7 @@ static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
                params_format(params));
 
        /* set cpu DAI configuration */
-       ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
+       ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
                SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
        if (ret < 0)
                return ret;
@@ -190,16 +190,16 @@ static struct snd_soc_dai_link bf5xx_ad73311_dai = {
        .ops = &bf5xx_ad73311_ops,
 };
 
-static struct snd_soc_machine bf5xx_ad73311 = {
+static struct snd_soc_card bf5xx_ad73311 = {
        .name = "bf5xx_ad73311",
+       .platform = &bf5xx_i2s_soc_platform,
        .probe = bf5xx_probe,
        .dai_link = &bf5xx_ad73311_dai,
        .num_links = 1,
 };
 
 static struct snd_soc_device bf5xx_ad73311_snd_devdata = {
-       .machine = &bf5xx_ad73311,
-       .platform = &bf5xx_i2s_soc_platform,
+       .card = &bf5xx_ad73311,
        .codec_dev = &soc_codec_dev_ad73311,
 };
 
index 61fccf9..53d290b 100644 (file)
@@ -283,6 +283,18 @@ struct snd_soc_platform bf5xx_i2s_soc_platform = {
 };
 EXPORT_SYMBOL_GPL(bf5xx_i2s_soc_platform);
 
+static int __init bfin_i2s_init(void)
+{
+       return snd_soc_register_platform(&bf5xx_i2s_soc_platform);
+}
+module_init(bfin_i2s_init);
+
+static void __exit bfin_i2s_exit(void)
+{
+       snd_soc_unregister_platform(&bf5xx_i2s_soc_platform);
+}
+module_exit(bfin_i2s_exit);
+
 MODULE_AUTHOR("Cliff Cai");
 MODULE_DESCRIPTION("ADI Blackfin I2S PCM DMA module");
 MODULE_LICENSE("GPL");
index e020c16..d1d95d2 100644 (file)
@@ -132,7 +132,8 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
        return ret;
 }
 
-static int bf5xx_i2s_startup(struct snd_pcm_substream *substream)
+static int bf5xx_i2s_startup(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
 {
        pr_debug("%s enter\n", __func__);
 
@@ -142,7 +143,8 @@ static int bf5xx_i2s_startup(struct snd_pcm_substream *substream)
 }
 
 static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
-                               struct snd_pcm_hw_params *params)
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
 {
        int ret = 0;
 
@@ -193,7 +195,8 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream)
+static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
 {
        pr_debug("%s enter\n", __func__);
        bf5xx_i2s.counter--;
@@ -219,16 +222,14 @@ static int bf5xx_i2s_probe(struct platform_device *pdev,
        return 0;
 }
 
-static void bf5xx_i2s_remove(struct platform_device *pdev,
-                          struct snd_soc_dai *dai)
+static void bf5xx_i2s_remove(struct snd_soc_dai *dai)
 {
        pr_debug("%s enter\n", __func__);
        peripheral_free_list(&sport_req[sport_num][0]);
 }
 
 #ifdef CONFIG_PM
-static int bf5xx_i2s_suspend(struct platform_device *dev,
-                            struct snd_soc_dai *dai)
+static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
 {
        struct sport_device *sport =
                (struct sport_device *)dai->private_data;
@@ -289,7 +290,6 @@ static int bf5xx_i2s_resume(struct platform_device *pdev,
 struct snd_soc_dai bf5xx_i2s_dai = {
        .name = "bf5xx-i2s",
        .id = 0,
-       .type = SND_SOC_DAI_I2S,
        .probe = bf5xx_i2s_probe,
        .remove = bf5xx_i2s_remove,
        .suspend = bf5xx_i2s_suspend,
@@ -307,13 +307,24 @@ struct snd_soc_dai bf5xx_i2s_dai = {
        .ops = {
                .startup   = bf5xx_i2s_startup,
                .shutdown  = bf5xx_i2s_shutdown,
-               .hw_params = bf5xx_i2s_hw_params,},
-       .dai_ops = {
+               .hw_params = bf5xx_i2s_hw_params,
                .set_fmt = bf5xx_i2s_set_dai_fmt,
        },
 };
 EXPORT_SYMBOL_GPL(bf5xx_i2s_dai);
 
+static int __init bfin_i2s_init(void)
+{
+       return snd_soc_register_dai(&bf5xx_i2s_dai);
+}
+module_init(bfin_i2s_init);
+
+static void __exit bfin_i2s_exit(void)
+{
+       snd_soc_unregister_dai(&bf5xx_i2s_dai);
+}
+module_exit(bfin_i2s_exit);
+
 /* Module information */
 MODULE_AUTHOR("Cliff Cai");
 MODULE_DESCRIPTION("I2S driver for ADI Blackfin");
index fcadcc0..2e63dea 100644 (file)
@@ -116,7 +116,7 @@ struct sport_device {
        void *err_data;
        unsigned char *tx_dma_buf;
        unsigned char *rx_dma_buf;
-#ifdef CONFIG_SND_MMAP_SUPPORT
+#ifdef CONFIG_SND_BF5XX_MMAP_SUPPORT
        dma_addr_t tx_dma_phy;
        dma_addr_t rx_dma_phy;
        int tx_pos;/*pcm sample count*/
index e15f67f..bc0cdde 100644 (file)
@@ -44,7 +44,7 @@
 #include "bf5xx-i2s-pcm.h"
 #include "bf5xx-i2s.h"
 
-static struct snd_soc_machine bf5xx_ssm2602;
+static struct snd_soc_card bf5xx_ssm2602;
 
 static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream)
 {
@@ -92,17 +92,17 @@ static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
         */
 
        /* set codec DAI configuration */
-       ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
                SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
        if (ret < 0)
                return ret;
        /* set cpu DAI configuration */
-       ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+       ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
                SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
        if (ret < 0)
                return ret;
 
-       ret = codec_dai->dai_ops.set_sysclk(codec_dai, SSM2602_SYSCLK, clk,
+       ret = snd_soc_dai_set_sysclk(codec_dai, SSM2602_SYSCLK, clk,
                SND_SOC_CLOCK_IN);
        if (ret < 0)
                return ret;
@@ -135,15 +135,15 @@ static struct ssm2602_setup_data bf5xx_ssm2602_setup = {
        .i2c_address = 0x1b,
 };
 
-static struct snd_soc_machine bf5xx_ssm2602 = {
+static struct snd_soc_card bf5xx_ssm2602 = {
        .name = "bf5xx_ssm2602",
+       .platform = &bf5xx_i2s_soc_platform,
        .dai_link = &bf5xx_ssm2602_dai,
        .num_links = 1,
 };
 
 static struct snd_soc_device bf5xx_ssm2602_snd_devdata = {
-       .machine = &bf5xx_ssm2602,
-       .platform = &bf5xx_i2s_soc_platform,
+       .card = &bf5xx_ssm2602,
        .codec_dev = &soc_codec_dev_ssm2602,
        .codec_data = &bf5xx_ssm2602_setup,
 };
index 38a0e3b..c41289b 100644 (file)
@@ -1,31 +1,40 @@
 config SND_SOC_ALL_CODECS
        tristate "Build all ASoC CODEC drivers"
-       depends on I2C
-       select SPI
-       select SPI_MASTER
-       select SND_SOC_AD73311
-       select SND_SOC_AK4535
-       select SND_SOC_CS4270
-       select SND_SOC_SSM2602
-       select SND_SOC_TLV320AIC23
-       select SND_SOC_TLV320AIC26
-       select SND_SOC_TLV320AIC3X
-       select SND_SOC_UDA1380
-       select SND_SOC_WM8510
-       select SND_SOC_WM8580
-       select SND_SOC_WM8731
-       select SND_SOC_WM8750
-       select SND_SOC_WM8753
-       select SND_SOC_WM8900
-       select SND_SOC_WM8903
-       select SND_SOC_WM8971
-       select SND_SOC_WM8990
+       select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
+       select SND_SOC_AD1980 if SND_SOC_AC97_BUS
+       select SND_SOC_AD73311 if I2C
+       select SND_SOC_AK4535 if I2C
+       select SND_SOC_CS4270 if I2C
+       select SND_SOC_PCM3008
+       select SND_SOC_SSM2602 if I2C
+       select SND_SOC_TLV320AIC23 if I2C
+       select SND_SOC_TLV320AIC26 if SPI_MASTER
+       select SND_SOC_TLV320AIC3X if I2C
+       select SND_SOC_TWL4030 if TWL4030_CORE
+       select SND_SOC_UDA134X
+       select SND_SOC_UDA1380 if I2C
+       select SND_SOC_WM8350 if MFD_WM8350
+       select SND_SOC_WM8510 if (I2C || SPI_MASTER)
+       select SND_SOC_WM8580 if I2C
+       select SND_SOC_WM8728 if (I2C || SPI_MASTER)
+       select SND_SOC_WM8731 if (I2C || SPI_MASTER)
+       select SND_SOC_WM8750 if (I2C || SPI_MASTER)
+       select SND_SOC_WM8753 if (I2C || SPI_MASTER)
+       select SND_SOC_WM8900 if I2C
+       select SND_SOC_WM8903 if I2C
+       select SND_SOC_WM8971 if I2C
+       select SND_SOC_WM8990 if I2C
+       select SND_SOC_WM9712 if SND_SOC_AC97_BUS
+       select SND_SOC_WM9713 if SND_SOC_AC97_BUS
         help
           Normally ASoC codec drivers are only built if a machine driver which
           uses them is also built since they are only usable with a machine
           driver.  Selecting this option will allow these drivers to be built
           without an explicit machine driver for test and development purposes.
 
+         Support for the bus types used to access the codecs to be built must
+         be selected separately.
+
           If unsure select "N".
 
 
@@ -60,6 +69,12 @@ config SND_SOC_CS4270_VD33_ERRATA
        bool
        depends on SND_SOC_CS4270
 
+config SND_SOC_L3
+       tristate
+
+config SND_SOC_PCM3008
+       tristate
+
 config SND_SOC_SSM2602
        tristate
 
@@ -75,15 +90,29 @@ config SND_SOC_TLV320AIC3X
        tristate
        depends on I2C
 
+config SND_SOC_TWL4030
+       tristate
+       depends on TWL4030_CORE
+
+config SND_SOC_UDA134X
+       tristate
+       select SND_SOC_L3
+
 config SND_SOC_UDA1380
         tristate
 
+config SND_SOC_WM8350
+       tristate
+
 config SND_SOC_WM8510
        tristate
 
 config SND_SOC_WM8580
        tristate
 
+config SND_SOC_WM8728
+       tristate
+
 config SND_SOC_WM8731
        tristate
 
index 90f0a58..c4ddc9a 100644 (file)
@@ -3,13 +3,19 @@ snd-soc-ad1980-objs := ad1980.o
 snd-soc-ad73311-objs := ad73311.o
 snd-soc-ak4535-objs := ak4535.o
 snd-soc-cs4270-objs := cs4270.o
+snd-soc-l3-objs := l3.o
+snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-ssm2602-objs := ssm2602.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic26-objs := tlv320aic26.o
 snd-soc-tlv320aic3x-objs := tlv320aic3x.o
+snd-soc-twl4030-objs := twl4030.o
+snd-soc-uda134x-objs := uda134x.o
 snd-soc-uda1380-objs := uda1380.o
+snd-soc-wm8350-objs := wm8350.o
 snd-soc-wm8510-objs := wm8510.o
 snd-soc-wm8580-objs := wm8580.o
+snd-soc-wm8728-objs := wm8728.o
 snd-soc-wm8731-objs := wm8731.o
 snd-soc-wm8750-objs := wm8750.o
 snd-soc-wm8753-objs := wm8753.o
@@ -25,13 +31,19 @@ obj-$(CONFIG_SND_SOC_AD1980)        += snd-soc-ad1980.o
 obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
 obj-$(CONFIG_SND_SOC_AK4535)   += snd-soc-ak4535.o
 obj-$(CONFIG_SND_SOC_CS4270)   += snd-soc-cs4270.o
+obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
+obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_SSM2602)  += snd-soc-ssm2602.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)      += snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC26)      += snd-soc-tlv320aic26.o
 obj-$(CONFIG_SND_SOC_TLV320AIC3X)      += snd-soc-tlv320aic3x.o
+obj-$(CONFIG_SND_SOC_TWL4030)  += snd-soc-twl4030.o
+obj-$(CONFIG_SND_SOC_UDA134X)  += snd-soc-uda134x.o
 obj-$(CONFIG_SND_SOC_UDA1380)  += snd-soc-uda1380.o
+obj-$(CONFIG_SND_SOC_WM8350)   += snd-soc-wm8350.o
 obj-$(CONFIG_SND_SOC_WM8510)   += snd-soc-wm8510.o
 obj-$(CONFIG_SND_SOC_WM8580)   += snd-soc-wm8580.o
+obj-$(CONFIG_SND_SOC_WM8728)   += snd-soc-wm8728.o
 obj-$(CONFIG_SND_SOC_WM8731)   += snd-soc-wm8731.o
 obj-$(CONFIG_SND_SOC_WM8750)   += snd-soc-wm8750.o
 obj-$(CONFIG_SND_SOC_WM8753)   += snd-soc-wm8753.o
index bd1ebdc..fb53e65 100644 (file)
@@ -24,7 +24,8 @@
 
 #define AC97_VERSION "0.6"
 
-static int ac97_prepare(struct snd_pcm_substream *substream)
+static int ac97_prepare(struct snd_pcm_substream *substream,
+                       struct snd_soc_dai *dai)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -42,7 +43,7 @@ static int ac97_prepare(struct snd_pcm_substream *substream)
 
 struct snd_soc_dai ac97_dai = {
        .name = "AC97 HiFi",
-       .type = SND_SOC_DAI_AC97,
+       .ac97_control = 1,
        .playback = {
                .stream_name = "AC97 Playback",
                .channels_min = 1,
@@ -113,7 +114,7 @@ static int ac97_soc_probe(struct platform_device *pdev)
        if (ret < 0)
                goto bus_err;
 
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0)
                goto bus_err;
        return 0;
index 1397b8e..73fdbb4 100644 (file)
@@ -85,6 +85,9 @@ SOC_DOUBLE("Line HP Swap Switch", AC97_AD_MISC, 10, 5, 1, 0),
 SOC_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1),
 SOC_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1),
 
+SOC_DOUBLE("Center/LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 0, 31, 1),
+SOC_DOUBLE("Center/LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 7, 1, 1),
+
 SOC_ENUM("Capture Source", ad1980_cap_src),
 
 SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0),
@@ -142,10 +145,11 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
 
 struct snd_soc_dai ad1980_dai = {
        .name = "AC97",
+       .ac97_control = 1,
        .playback = {
                .stream_name = "Playback",
                .channels_min = 2,
-               .channels_max = 2,
+               .channels_max = 6,
                .rates = SNDRV_PCM_RATE_48000,
                .formats = SNDRV_PCM_FMTBIT_S16_LE, },
        .capture = {
@@ -192,6 +196,7 @@ static int ad1980_soc_probe(struct platform_device *pdev)
        struct snd_soc_codec *codec;
        int ret = 0;
        u16 vendor_id2;
+       u16 ext_status;
 
        printk(KERN_INFO "AD1980 SoC Audio Codec\n");
 
@@ -234,7 +239,7 @@ static int ad1980_soc_probe(struct platform_device *pdev)
 
        ret = ad1980_reset(codec, 0);
        if (ret < 0) {
-               printk(KERN_ERR "AC97 link error\n");
+               printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n");
                goto reset_err;
        }
 
@@ -253,12 +258,19 @@ static int ad1980_soc_probe(struct platform_device *pdev)
                                "supported\n");
        }
 
-       ac97_write(codec, AC97_MASTER, 0x0000); /* unmute line out volume */
-       ac97_write(codec, AC97_PCM, 0x0000);    /* unmute PCM out volume */
-       ac97_write(codec, AC97_REC_GAIN, 0x0000);/* unmute record volume */
+       /* unmute captures and playbacks volume */
+       ac97_write(codec, AC97_MASTER, 0x0000);
+       ac97_write(codec, AC97_PCM, 0x0000);
+       ac97_write(codec, AC97_REC_GAIN, 0x0000);
+       ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
+       ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);
+
+       /*power on LFE/CENTER/Surround DACs*/
+       ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
+       ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
 
        ad1980_add_controls(codec);
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                printk(KERN_ERR "ad1980: failed to register card\n");
                goto reset_err;
index 37af860..b09289a 100644 (file)
@@ -8,14 +8,10 @@
  *  under  the terms of  the GNU General  Public License as published by the
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
- *
- *  Revision history
- *    25th Sep 2008   Initial version.
  */
 
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
 #include <sound/core.h>
@@ -68,7 +64,7 @@ static int ad73311_soc_probe(struct platform_device *pdev)
                goto pcm_err;
        }
 
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                printk(KERN_ERR "ad73311: failed to register card\n");
                goto register_err;
@@ -102,6 +98,18 @@ struct snd_soc_codec_device soc_codec_dev_ad73311 = {
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_ad73311);
 
+static int __init ad73311_init(void)
+{
+       return snd_soc_register_dai(&ad73311_dai);
+}
+module_init(ad73311_init);
+
+static void __exit ad73311_exit(void)
+{
+       snd_soc_unregister_dai(&ad73311_dai);
+}
+module_exit(ad73311_exit);
+
 MODULE_DESCRIPTION("ASoC ad73311 driver");
 MODULE_AUTHOR("Cliff Cai ");
 MODULE_LICENSE("GPL");
index 2a89b58..81300d8 100644 (file)
@@ -339,7 +339,8 @@ static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 }
 
 static int ak4535_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -451,8 +452,6 @@ struct snd_soc_dai ak4535_dai = {
                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
        .ops = {
                .hw_params = ak4535_hw_params,
-       },
-       .dai_ops = {
                .set_fmt = ak4535_set_dai_fmt,
                .digital_mute = ak4535_mute,
                .set_sysclk = ak4535_set_dai_sysclk,
@@ -513,7 +512,7 @@ static int ak4535_init(struct snd_soc_device *socdev)
 
        ak4535_add_controls(codec);
        ak4535_add_widgets(codec);
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                printk(KERN_ERR "ak4535: failed to register card\n");
                goto card_err;
@@ -689,6 +688,18 @@ struct snd_soc_codec_device soc_codec_dev_ak4535 = {
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_ak4535);
 
+static int __init ak4535_modinit(void)
+{
+       return snd_soc_register_dai(&ak4535_dai);
+}
+module_init(ak4535_modinit);
+
+static void __exit ak4535_exit(void)
+{
+       snd_soc_unregister_dai(&ak4535_dai);
+}
+module_exit(ak4535_exit);
+
 MODULE_DESCRIPTION("Soc AK4535 driver");
 MODULE_AUTHOR("Richard Purdie");
 MODULE_LICENSE("GPL");
index 0bbd945..f1aa0c3 100644 (file)
@@ -360,13 +360,14 @@ static int cs4270_i2c_write(struct snd_soc_codec *codec, unsigned int reg,
 /*
  * Program the CS4270 with the given hardware parameters.
  *
- * The .dai_ops functions are used to provide board-specific data, like
+ * The .ops functions are used to provide board-specific data, like
  * input frequencies, to this driver.  This function takes that information,
  * combines it with the hardware parameters provided, and programs the
  * hardware accordingly.
  */
 static int cs4270_hw_params(struct snd_pcm_substream *substream,
-                           struct snd_pcm_hw_params *params)
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -450,6 +451,19 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
                return ret;
        }
 
+       /* Disable automatic volume control.  It's enabled by default, and
+        * it causes volume change commands to be delayed, sometimes until
+        * after playback has started.
+        */
+
+       reg = cs4270_read_reg_cache(codec, CS4270_TRANS);
+       reg &= ~(CS4270_TRANS_SOFT | CS4270_TRANS_ZERO);
+       ret = cs4270_i2c_write(codec, CS4270_TRANS, reg);
+       if (ret < 0) {
+               printk(KERN_ERR "I2C write failed\n");
+               return ret;
+       }
+
        /* Thaw and power-up the codec */
 
        ret = snd_soc_write(codec, CS4270_PWRCTL, 0);
@@ -697,10 +711,10 @@ static int cs4270_probe(struct platform_device *pdev)
        if (codec->control_data) {
                /* Initialize codec ops */
                cs4270_dai.ops.hw_params = cs4270_hw_params;
-               cs4270_dai.dai_ops.set_sysclk = cs4270_set_dai_sysclk;
-               cs4270_dai.dai_ops.set_fmt = cs4270_set_dai_fmt;
+               cs4270_dai.ops.set_sysclk = cs4270_set_dai_sysclk;
+               cs4270_dai.ops.set_fmt = cs4270_set_dai_fmt;
 #ifdef CONFIG_SND_SOC_CS4270_HWMUTE
-               cs4270_dai.dai_ops.digital_mute = cs4270_mute;
+               cs4270_dai.ops.digital_mute = cs4270_mute;
 #endif
        } else
                printk(KERN_INFO "cs4270: no I2C device found, "
@@ -709,7 +723,7 @@ static int cs4270_probe(struct platform_device *pdev)
        printk(KERN_INFO "cs4270: I2C disabled, using stand-alone mode\n");
 #endif
 
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                printk(KERN_ERR "cs4270: failed to register card\n");
                goto error_del_driver;
@@ -760,6 +774,18 @@ struct snd_soc_codec_device soc_codec_device_cs4270 = {
 };
 EXPORT_SYMBOL_GPL(soc_codec_device_cs4270);
 
+static int __init cs4270_init(void)
+{
+       return snd_soc_register_dai(&cs4270_dai);
+}
+module_init(cs4270_init);
+
+static void __exit cs4270_exit(void)
+{
+       snd_soc_unregister_dai(&cs4270_dai);
+}
+module_exit(cs4270_exit);
+
 MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
 MODULE_DESCRIPTION("Cirrus Logic CS4270 ALSA SoC Codec Driver");
 MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/l3.c b/sound/soc/codecs/l3.c
new file mode 100644 (file)
index 0000000..5353af5
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * L3 code
+ *
+ *  Copyright (C) 2008, Christian Pellegrin <chripell@evolware.org>
+ *
+ * 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.
+ *
+ *
+ * based on:
+ *
+ * L3 bus algorithm module.
+ *
+ *  Copyright (C) 2001 Russell King, All Rights Reserved.
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+#include <sound/l3.h>
+
+/*
+ * Send one byte of data to the chip.  Data is latched into the chip on
+ * the rising edge of the clock.
+ */
+static void sendbyte(struct l3_pins *adap, unsigned int byte)
+{
+       int i;
+
+       for (i = 0; i < 8; i++) {
+               adap->setclk(0);
+               udelay(adap->data_hold);
+               adap->setdat(byte & 1);
+               udelay(adap->data_setup);
+               adap->setclk(1);
+               udelay(adap->clock_high);
+               byte >>= 1;
+       }
+}
+
+/*
+ * Send a set of bytes to the chip.  We need to pulse the MODE line
+ * between each byte, but never at the start nor at the end of the
+ * transfer.
+ */
+static void sendbytes(struct l3_pins *adap, const u8 *buf,
+                     int len)
+{
+       int i;
+
+       for (i = 0; i < len; i++) {
+               if (i) {
+                       udelay(adap->mode_hold);
+                       adap->setmode(0);
+                       udelay(adap->mode);
+               }
+               adap->setmode(1);
+               udelay(adap->mode_setup);
+               sendbyte(adap, buf[i]);
+       }
+}
+
+int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len)
+{
+       adap->setclk(1);
+       adap->setdat(1);
+       adap->setmode(1);
+       udelay(adap->mode);
+
+       adap->setmode(0);
+       udelay(adap->mode_setup);
+       sendbyte(adap, addr);
+       udelay(adap->mode_hold);
+
+       sendbytes(adap, data, len);
+
+       adap->setclk(1);
+       adap->setdat(1);
+       adap->setmode(0);
+
+       return len;
+}
+EXPORT_SYMBOL_GPL(l3_write);
+
+MODULE_DESCRIPTION("L3 bit-banging driver");
+MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c
new file mode 100644 (file)
index 0000000..9a3e67e
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * ALSA Soc PCM3008 codec support
+ *
+ * Author:     Hugo Villeneuve
+ * Copyright (C) 2008 Lyrtech inc
+ *
+ * Based on AC97 Soc codec, original copyright follow:
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ * Generic PCM3008 support.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "pcm3008.h"
+
+#define PCM3008_VERSION "0.2"
+
+#define PCM3008_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |   \
+                      SNDRV_PCM_RATE_48000)
+
+struct snd_soc_dai pcm3008_dai = {
+       .name = "PCM3008 HiFi",
+       .playback = {
+               .stream_name = "PCM3008 Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = PCM3008_RATES,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "PCM3008 Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = PCM3008_RATES,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+};
+EXPORT_SYMBOL_GPL(pcm3008_dai);
+
+static void pcm3008_gpio_free(struct pcm3008_setup_data *setup)
+{
+       gpio_free(setup->dem0_pin);
+       gpio_free(setup->dem1_pin);
+       gpio_free(setup->pdad_pin);
+       gpio_free(setup->pdda_pin);
+}
+
+static int pcm3008_soc_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       struct pcm3008_setup_data *setup = socdev->codec_data;
+       int ret = 0;
+
+       printk(KERN_INFO "PCM3008 SoC Audio Codec %s\n", PCM3008_VERSION);
+
+       socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+       if (!socdev->codec)
+               return -ENOMEM;
+
+       codec = socdev->codec;
+       mutex_init(&codec->mutex);
+
+       codec->name = "PCM3008";
+       codec->owner = THIS_MODULE;
+       codec->dai = &pcm3008_dai;
+       codec->num_dai = 1;
+       codec->write = NULL;
+       codec->read = NULL;
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       /* Register PCMs. */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               printk(KERN_ERR "pcm3008: failed to create pcms\n");
+               goto pcm_err;
+       }
+
+       /* Register Card. */
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               printk(KERN_ERR "pcm3008: failed to register card\n");
+               goto card_err;
+       }
+
+       /* DEM1  DEM0  DE-EMPHASIS_MODE
+        * Low   Low   De-emphasis 44.1 kHz ON
+        * Low   High  De-emphasis OFF
+        * High  Low   De-emphasis 48 kHz ON
+        * High  High  De-emphasis 32 kHz ON
+        */
+
+       /* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */
+       ret = gpio_request(setup->dem0_pin, "codec_dem0");
+       if (ret == 0)
+               ret = gpio_direction_output(setup->dem0_pin, 1);
+       if (ret != 0)
+               goto gpio_err;
+
+       /* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */
+       ret = gpio_request(setup->dem1_pin, "codec_dem1");
+       if (ret == 0)
+               ret = gpio_direction_output(setup->dem1_pin, 0);
+       if (ret != 0)
+               goto gpio_err;
+
+       /* Configure PDAD GPIO. */
+       ret = gpio_request(setup->pdad_pin, "codec_pdad");
+       if (ret == 0)
+               ret = gpio_direction_output(setup->pdad_pin, 1);
+       if (ret != 0)
+               goto gpio_err;
+
+       /* Configure PDDA GPIO. */
+       ret = gpio_request(setup->pdda_pin, "codec_pdda");
+       if (ret == 0)
+               ret = gpio_direction_output(setup->pdda_pin, 1);
+       if (ret != 0)
+               goto gpio_err;
+
+       return ret;
+
+gpio_err:
+       pcm3008_gpio_free(setup);
+card_err:
+       snd_soc_free_pcms(socdev);
+pcm_err:
+       kfree(socdev->codec);
+
+       return ret;
+}
+
+static int pcm3008_soc_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->codec;
+       struct pcm3008_setup_data *setup = socdev->codec_data;
+
+       if (!codec)
+               return 0;
+
+       pcm3008_gpio_free(setup);
+       snd_soc_free_pcms(socdev);
+       kfree(socdev->codec);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int pcm3008_soc_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct pcm3008_setup_data *setup = socdev->codec_data;
+
+       gpio_set_value(setup->pdad_pin, 0);
+       gpio_set_value(setup->pdda_pin, 0);
+
+       return 0;
+}
+
+static int pcm3008_soc_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct pcm3008_setup_data *setup = socdev->codec_data;
+
+       gpio_set_value(setup->pdad_pin, 1);
+       gpio_set_value(setup->pdda_pin, 1);
+
+       return 0;
+}
+#else
+#define pcm3008_soc_suspend NULL
+#define pcm3008_soc_resume NULL
+#endif
+
+struct snd_soc_codec_device soc_codec_dev_pcm3008 = {
+       .probe =        pcm3008_soc_probe,
+       .remove =       pcm3008_soc_remove,
+       .suspend =      pcm3008_soc_suspend,
+       .resume =       pcm3008_soc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_pcm3008);
+
+static int __init pcm3008_init(void)
+{
+       return snd_soc_register_dai(&pcm3008_dai);
+}
+module_init(pcm3008_init);
+
+static void __exit pcm3008_exit(void)
+{
+       snd_soc_unregister_dai(&pcm3008_dai);
+}
+module_exit(pcm3008_exit);
+
+MODULE_DESCRIPTION("Soc PCM3008 driver");
+MODULE_AUTHOR("Hugo Villeneuve");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm3008.h b/sound/soc/codecs/pcm3008.h
new file mode 100644 (file)
index 0000000..d04e87d
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * PCM3008 ALSA SoC Layer
+ *
+ * Author:     Hugo Villeneuve
+ * Copyright (C) 2008 Lyrtech inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_SND_SOC_PCM3008_H
+#define __LINUX_SND_SOC_PCM3008_H
+
+struct pcm3008_setup_data {
+       unsigned dem0_pin;
+       unsigned dem1_pin;
+       unsigned pdad_pin;
+       unsigned pdda_pin;
+};
+
+extern struct snd_soc_codec_device soc_codec_dev_pcm3008;
+extern struct snd_soc_dai pcm3008_dai;
+
+#endif
index 44ef0da..cac3736 100644 (file)
@@ -285,16 +285,23 @@ static inline int get_coeff(int mclk, int rate)
 }
 
 static int ssm2602_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
+       struct snd_pcm_hw_params *params,
+       struct snd_soc_dai *dai)
 {
        u16 srate;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
        struct snd_soc_codec *codec = socdev->codec;
        struct ssm2602_priv *ssm2602 = codec->private_data;
+       struct i2c_client *i2c = codec->control_data;
        u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3;
        int i = get_coeff(ssm2602->sysclk, params_rate(params));
 
+       if (substream == ssm2602->slave_substream) {
+               dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n");
+               return 0;
+       }
+
        /*no match is found*/
        if (i == ARRAY_SIZE(coeff_div))
                return -EINVAL;
@@ -324,19 +331,26 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int ssm2602_startup(struct snd_pcm_substream *substream)
+static int ssm2602_startup(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
        struct snd_soc_codec *codec = socdev->codec;
        struct ssm2602_priv *ssm2602 = codec->private_data;
+       struct i2c_client *i2c = codec->control_data;
        struct snd_pcm_runtime *master_runtime;
 
        /* The DAI has shared clocks so if we already have a playback or
         * capture going then constrain this substream to match it.
+        * TODO: the ssm2602 allows pairs of non-matching PB/REC rates
         */
        if (ssm2602->master_substream) {
                master_runtime = ssm2602->master_substream->runtime;
+               dev_dbg(&i2c->dev, "Constraining to %d bits at %dHz\n",
+                       master_runtime->sample_bits,
+                       master_runtime->rate);
+
                snd_pcm_hw_constraint_minmax(substream->runtime,
                                             SNDRV_PCM_HW_PARAM_RATE,
                                             master_runtime->rate,
@@ -354,7 +368,8 @@ static int ssm2602_startup(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream)
+static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -365,14 +380,21 @@ static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static void ssm2602_shutdown(struct snd_pcm_substream *substream)
+static void ssm2602_shutdown(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
        struct snd_soc_codec *codec = socdev->codec;
+       struct ssm2602_priv *ssm2602 = codec->private_data;
        /* deactivate */
        if (!codec->active)
                ssm2602_write(codec, SSM2602_ACTIVE, 0);
+
+       if (ssm2602->master_substream == substream)
+               ssm2602->master_substream = ssm2602->slave_substream;
+
+       ssm2602->slave_substream = NULL;
 }
 
 static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
@@ -432,10 +454,10 @@ static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
                iface |= 0x0001;
                break;
        case SND_SOC_DAIFMT_DSP_A:
-               iface |= 0x0003;
+               iface |= 0x0013;
                break;
        case SND_SOC_DAIFMT_DSP_B:
-               iface |= 0x0013;
+               iface |= 0x0003;
                break;
        default:
                return -EINVAL;
@@ -496,6 +518,9 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
                SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
                SNDRV_PCM_RATE_96000)
 
+#define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+               SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
 struct snd_soc_dai ssm2602_dai = {
        .name = "SSM2602",
        .playback = {
@@ -503,20 +528,18 @@ struct snd_soc_dai ssm2602_dai = {
                .channels_min = 2,
                .channels_max = 2,
                .rates = SSM2602_RATES,
-               .formats = SNDRV_PCM_FMTBIT_S32_LE,},
+               .formats = SSM2602_FORMATS,},
        .capture = {
                .stream_name = "Capture",
                .channels_min = 2,
                .channels_max = 2,
                .rates = SSM2602_RATES,
-               .formats = SNDRV_PCM_FMTBIT_S32_LE,},
+               .formats = SSM2602_FORMATS,},
        .ops = {
                .startup = ssm2602_startup,
                .prepare = ssm2602_pcm_prepare,
                .hw_params = ssm2602_hw_params,
                .shutdown = ssm2602_shutdown,
-       },
-       .dai_ops = {
                .digital_mute = ssm2602_mute,
                .set_sysclk = ssm2602_set_dai_sysclk,
                .set_fmt = ssm2602_set_dai_fmt,
@@ -601,7 +624,7 @@ static int ssm2602_init(struct snd_soc_device *socdev)
 
        ssm2602_add_controls(codec);
        ssm2602_add_widgets(codec);
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                pr_err("ssm2602: failed to register card\n");
                goto card_err;
@@ -770,6 +793,18 @@ struct snd_soc_codec_device soc_codec_dev_ssm2602 = {
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_ssm2602);
 
+static int __init ssm2602_modinit(void)
+{
+       return snd_soc_register_dai(&ssm2602_dai);
+}
+module_init(ssm2602_modinit);
+
+static void __exit ssm2602_exit(void)
+{
+       snd_soc_unregister_dai(&ssm2602_dai);
+}
+module_exit(ssm2602_exit);
+
 MODULE_DESCRIPTION("ASoC ssm2602 driver");
 MODULE_AUTHOR("Cliff Cai");
 MODULE_LICENSE("GPL");
index 44308da..cfdea00 100644 (file)
 
 #define AIC23_VERSION "0.1"
 
-struct tlv320aic23_srate_reg_info {
-       u32 sample_rate;
-       u8 control;             /* SR3, SR2, SR1, SR0 and BOSR */
-       u8 divider;             /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */
-};
-
 /*
  * AIC23 register cache
  */
@@ -261,20 +255,156 @@ static const struct snd_soc_dapm_route intercon[] = {
 
 };
 
-/* tlv320aic23 related */
-static const struct tlv320aic23_srate_reg_info srate_reg_info[] = {
-       {4000, 0x06, 1},        /*  4000 */
-       {8000, 0x06, 0},        /*  8000 */
-       {16000, 0x0C, 1},       /* 16000 */
-       {22050, 0x11, 1},       /* 22050 */
-       {24000, 0x00, 1},       /* 24000 */
-       {32000, 0x0C, 0},       /* 32000 */
-       {44100, 0x11, 0},       /* 44100 */
-       {48000, 0x00, 0},       /* 48000 */
-       {88200, 0x1F, 0},       /* 88200 */
-       {96000, 0x0E, 0},       /* 96000 */
+/* AIC23 driver data */
+struct aic23 {
+       struct snd_soc_codec codec;
+       int mclk;
+       int requested_adc;
+       int requested_dac;
+};
+
+/*
+ * Common Crystals used
+ * 11.2896 Mhz /128 = *88.2k  /192 = 58.8k
+ * 12.0000 Mhz /125 = *96k    /136 = 88.235K
+ * 12.2880 Mhz /128 = *96k    /192 = 64k
+ * 16.9344 Mhz /128 = 132.3k /192 = *88.2k
+ * 18.4320 Mhz /128 = 144k   /192 = *96k
+ */
+
+/*
+ * Normal BOSR 0-256/2 = 128, 1-384/2 = 192
+ * USB BOSR 0-250/2 = 125, 1-272/2 = 136
+ */
+static const int bosr_usb_divisor_table[] = {
+       128, 125, 192, 136
+};
+#define LOWER_GROUP ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<6) | (1<<7))
+#define UPPER_GROUP ((1<<8) | (1<<9) | (1<<10) | (1<<11)        | (1<<15))
+static const unsigned short sr_valid_mask[] = {
+       LOWER_GROUP|UPPER_GROUP,        /* Normal, bosr - 0*/
+       LOWER_GROUP|UPPER_GROUP,        /* Normal, bosr - 1*/
+       LOWER_GROUP,                    /* Usb, bosr - 0*/
+       UPPER_GROUP,                    /* Usb, bosr - 1*/
+};
+/*
+ * Every divisor is a factor of 11*12
+ */
+#define SR_MULT (11*12)
+#define A(x) (x) ? (SR_MULT/x) : 0
+static const unsigned char sr_adc_mult_table[] = {
+       A(2), A(2), A(12), A(12),  A(0), A(0), A(3), A(1),
+       A(2), A(2), A(11), A(11),  A(0), A(0), A(0), A(1)
+};
+static const unsigned char sr_dac_mult_table[] = {
+       A(2), A(12), A(2), A(12),  A(0), A(0), A(3), A(1),
+       A(2), A(11), A(2), A(11),  A(0), A(0), A(0), A(1)
 };
 
+static unsigned get_score(int adc, int adc_l, int adc_h, int need_adc,
+               int dac, int dac_l, int dac_h, int need_dac)
+{
+       if ((adc >= adc_l) && (adc <= adc_h) &&
+                       (dac >= dac_l) && (dac <= dac_h)) {
+               int diff_adc = need_adc - adc;
+               int diff_dac = need_dac - dac;
+               return abs(diff_adc) + abs(diff_dac);
+       }
+       return UINT_MAX;
+}
+
+static int find_rate(int mclk, u32 need_adc, u32 need_dac)
+{
+       int i, j;
+       int best_i = -1;
+       int best_j = -1;
+       int best_div = 0;
+       unsigned best_score = UINT_MAX;
+       int adc_l, adc_h, dac_l, dac_h;
+
+       need_adc *= SR_MULT;
+       need_dac *= SR_MULT;
+       /*
+        * rates given are +/- 1/32
+        */
+       adc_l = need_adc - (need_adc >> 5);
+       adc_h = need_adc + (need_adc >> 5);
+       dac_l = need_dac - (need_dac >> 5);
+       dac_h = need_dac + (need_dac >> 5);
+       for (i = 0; i < ARRAY_SIZE(bosr_usb_divisor_table); i++) {
+               int base = mclk / bosr_usb_divisor_table[i];
+               int mask = sr_valid_mask[i];
+               for (j = 0; j < ARRAY_SIZE(sr_adc_mult_table);
+                               j++, mask >>= 1) {
+                       int adc;
+                       int dac;
+                       int score;
+                       if ((mask & 1) == 0)
+                               continue;
+                       adc = base * sr_adc_mult_table[j];
+                       dac = base * sr_dac_mult_table[j];
+                       score = get_score(adc, adc_l, adc_h, need_adc,
+                                       dac, dac_l, dac_h, need_dac);
+                       if (best_score > score) {
+                               best_score = score;
+                               best_i = i;
+                               best_j = j;
+                               best_div = 0;
+                       }
+                       score = get_score((adc >> 1), adc_l, adc_h, need_adc,
+                                       (dac >> 1), dac_l, dac_h, need_dac);
+                       /* prefer to have a /2 */
+                       if ((score != UINT_MAX) && (best_score >= score)) {
+                               best_score = score;
+                               best_i = i;
+                               best_j = j;
+                               best_div = 1;
+                       }
+               }
+       }
+       return (best_j << 2) | best_i | (best_div << TLV320AIC23_CLKIN_SHIFT);
+}
+
+#ifdef DEBUG
+static void get_current_sample_rates(struct snd_soc_codec *codec, int mclk,
+               u32 *sample_rate_adc, u32 *sample_rate_dac)
+{
+       int src = tlv320aic23_read_reg_cache(codec, TLV320AIC23_SRATE);
+       int sr = (src >> 2) & 0x0f;
+       int val = (mclk / bosr_usb_divisor_table[src & 3]);
+       int adc = (val * sr_adc_mult_table[sr]) / SR_MULT;
+       int dac = (val * sr_dac_mult_table[sr]) / SR_MULT;
+       if (src & TLV320AIC23_CLKIN_HALF) {
+               adc >>= 1;
+               dac >>= 1;
+       }
+       *sample_rate_adc = adc;
+       *sample_rate_dac = dac;
+}
+#endif
+
+static int set_sample_rate_control(struct snd_soc_codec *codec, int mclk,
+               u32 sample_rate_adc, u32 sample_rate_dac)
+{
+       /* Search for the right sample rate */
+       int data = find_rate(mclk, sample_rate_adc, sample_rate_dac);
+       if (data < 0) {
+               printk(KERN_ERR "%s:Invalid rate %u,%u requested\n",
+                               __func__, sample_rate_adc, sample_rate_dac);
+               return -EINVAL;
+       }
+       tlv320aic23_write(codec, TLV320AIC23_SRATE, data);
+#ifdef DEBUG
+       {
+               u32 adc, dac;
+               get_current_sample_rates(codec, mclk, &adc, &dac);
+               printk(KERN_DEBUG "actual samplerate = %u,%u reg=%x\n",
+                       adc, dac, data);
+       }
+#endif
+       return 0;
+}
+
 static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
 {
        snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
@@ -288,32 +418,36 @@ static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
 }
 
 static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
-                                struct snd_pcm_hw_params *params)
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
        struct snd_soc_codec *codec = socdev->codec;
-       u16 iface_reg, data;
-       u8 count = 0;
+       u16 iface_reg;
+       int ret;
+       struct aic23 *aic23 = container_of(codec, struct aic23, codec);
+       u32 sample_rate_adc = aic23->requested_adc;
+       u32 sample_rate_dac = aic23->requested_dac;
+       u32 sample_rate = params_rate(params);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               aic23->requested_dac = sample_rate_dac = sample_rate;
+               if (!sample_rate_adc)
+                       sample_rate_adc = sample_rate;
+       } else {
+               aic23->requested_adc = sample_rate_adc = sample_rate;
+               if (!sample_rate_dac)
+                       sample_rate_dac = sample_rate;
+       }
+       ret = set_sample_rate_control(codec, aic23->mclk, sample_rate_adc,
+                       sample_rate_dac);
+       if (ret < 0)
+               return ret;
 
        iface_reg =
            tlv320aic23_read_reg_cache(codec,
                                       TLV320AIC23_DIGT_FMT) & ~(0x03 << 2);
-
-       /* Search for the right sample rate */
-       /* Verify what happens if the rate is not supported
-        * now it goes to 96Khz */
-       while ((srate_reg_info[count].sample_rate != params_rate(params)) &&
-              (count < ARRAY_SIZE(srate_reg_info))) {
-               count++;
-       }
-
-       data =  (srate_reg_info[count].divider << TLV320AIC23_CLKIN_SHIFT) |
-               (srate_reg_info[count]. control << TLV320AIC23_BOSR_SHIFT) |
-               TLV320AIC23_USB_CLK_ON;
-
-       tlv320aic23_write(codec, TLV320AIC23_SRATE, data);
-
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S16_LE:
                break;
@@ -332,7 +466,8 @@ static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream)
+static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream,
+                                  struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -344,17 +479,23 @@ static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static void tlv320aic23_shutdown(struct snd_pcm_substream *substream)
+static void tlv320aic23_shutdown(struct snd_pcm_substream *substream,
+                                struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
        struct snd_soc_codec *codec = socdev->codec;
+       struct aic23 *aic23 = container_of(codec, struct aic23, codec);
 
        /* deactivate */
        if (!codec->active) {
                udelay(50);
                tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0);
        }
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               aic23->requested_dac = 0;
+       else
+               aic23->requested_adc = 0;
 }
 
 static int tlv320aic23_mute(struct snd_soc_dai *dai, int mute)
@@ -400,7 +541,7 @@ static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai,
        case SND_SOC_DAIFMT_I2S:
                iface_reg |= TLV320AIC23_FOR_I2S;
                break;
-       case SND_SOC_DAIFMT_DSP_A:
+       case SND_SOC_DAIFMT_DSP_B:
                iface_reg |= TLV320AIC23_FOR_DSP;
                break;
        case SND_SOC_DAIFMT_RIGHT_J:
@@ -422,12 +563,9 @@ static int tlv320aic23_set_dai_sysclk(struct snd_soc_dai *codec_dai,
                                      int clk_id, unsigned int freq, int dir)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
-
-       switch (freq) {
-       case 12000000:
-               return 0;
-       }
-       return -EINVAL;
+       struct aic23 *aic23 = container_of(codec, struct aic23, codec);
+       aic23->mclk = freq;
+       return 0;
 }
 
 static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec,
@@ -478,12 +616,10 @@ struct snd_soc_dai tlv320aic23_dai = {
                .prepare = tlv320aic23_pcm_prepare,
                .hw_params = tlv320aic23_hw_params,
                .shutdown = tlv320aic23_shutdown,
-               },
-       .dai_ops = {
-                   .digital_mute = tlv320aic23_mute,
-                   .set_fmt = tlv320aic23_set_dai_fmt,
-                   .set_sysclk = tlv320aic23_set_dai_sysclk,
-                   }
+               .digital_mute = tlv320aic23_mute,
+               .set_fmt = tlv320aic23_set_dai_fmt,
+               .set_sysclk = tlv320aic23_set_dai_sysclk,
+       }
 };
 EXPORT_SYMBOL_GPL(tlv320aic23_dai);
 
@@ -584,7 +720,7 @@ static int tlv320aic23_init(struct snd_soc_device *socdev)
 
        tlv320aic23_add_controls(codec);
        tlv320aic23_add_widgets(codec);
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                printk(KERN_ERR "tlv320aic23: failed to register card\n");
                goto card_err;
@@ -659,14 +795,15 @@ static int tlv320aic23_probe(struct platform_device *pdev)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_codec *codec;
+       struct aic23 *aic23;
        int ret = 0;
 
        printk(KERN_INFO "AIC23 Audio Codec %s\n", AIC23_VERSION);
 
-       codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-       if (codec == NULL)
+       aic23 = kzalloc(sizeof(struct aic23), GFP_KERNEL);
+       if (aic23 == NULL)
                return -ENOMEM;
-
+       codec = &aic23->codec;
        socdev->codec = codec;
        mutex_init(&codec->mutex);
        INIT_LIST_HEAD(&codec->dapm_widgets);
@@ -687,6 +824,7 @@ static int tlv320aic23_remove(struct platform_device *pdev)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_codec *codec = socdev->codec;
+       struct aic23 *aic23 = container_of(codec, struct aic23, codec);
 
        if (codec->control_data)
                tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
@@ -697,7 +835,7 @@ static int tlv320aic23_remove(struct platform_device *pdev)
        i2c_del_driver(&tlv320aic23_i2c_driver);
 #endif
        kfree(codec->reg_cache);
-       kfree(codec);
+       kfree(aic23);
 
        return 0;
 }
@@ -709,6 +847,18 @@ struct snd_soc_codec_device soc_codec_dev_tlv320aic23 = {
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320aic23);
 
+static int __init tlv320aic23_modinit(void)
+{
+       return snd_soc_register_dai(&tlv320aic23_dai);
+}
+module_init(tlv320aic23_modinit);
+
+static void __exit tlv320aic23_exit(void)
+{
+       snd_soc_unregister_dai(&tlv320aic23_dai);
+}
+module_exit(tlv320aic23_exit);
+
 MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver");
 MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
 MODULE_LICENSE("GPL");
index bed8a9e..29f2f1a 100644 (file)
@@ -125,7 +125,8 @@ static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg,
  * Digital Audio Interface Operations
  */
 static int aic26_hw_params(struct snd_pcm_substream *substream,
-                          struct snd_pcm_hw_params *params)
+                          struct snd_pcm_hw_params *params,
+                          struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -287,8 +288,6 @@ struct snd_soc_dai aic26_dai = {
        },
        .ops = {
                .hw_params = aic26_hw_params,
-       },
-       .dai_ops = {
                .digital_mute = aic26_mute,
                .set_sysclk = aic26_set_sysclk,
                .set_fmt = aic26_set_fmt,
@@ -360,7 +359,7 @@ static int aic26_probe(struct platform_device *pdev)
 
        /* CODEC is setup, we can register the card now */
        dev_dbg(&pdev->dev, "Registering card\n");
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                dev_err(&pdev->dev, "aic26: failed to register card\n");
                goto card_err;
@@ -427,7 +426,7 @@ static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set);
 static int aic26_spi_probe(struct spi_device *spi)
 {
        struct aic26 *aic26;
-       int rc, i, reg;
+       int ret, i, reg;
 
        dev_dbg(&spi->dev, "probing tlv320aic26 spi device\n");
 
@@ -457,6 +456,14 @@ static int aic26_spi_probe(struct spi_device *spi)
        aic26->codec.reg_cache_size = AIC26_NUM_REGS;
        aic26->codec.reg_cache = aic26->reg_cache;
 
+       aic26_dai.dev = &spi->dev;
+       ret = snd_soc_register_dai(&aic26_dai);
+       if (ret != 0) {
+               dev_err(&spi->dev, "Failed to register DAI: %d\n", ret);
+               kfree(aic26);
+               return ret;
+       }
+
        /* Reset the codec to power on defaults */
        aic26_reg_write(&aic26->codec, AIC26_REG_RESET, 0xBB00);
 
@@ -475,8 +482,8 @@ static int aic26_spi_probe(struct spi_device *spi)
 
        /* Register the sysfs files for debugging */
        /* Create SysFS files */
-       rc = device_create_file(&spi->dev, &dev_attr_keyclick);
-       if (rc)
+       ret = device_create_file(&spi->dev, &dev_attr_keyclick);
+       if (ret)
                dev_info(&spi->dev, "error creating sysfs files\n");
 
 #if defined(CONFIG_SND_SOC_OF_SIMPLE)
@@ -493,6 +500,7 @@ static int aic26_spi_remove(struct spi_device *spi)
 {
        struct aic26 *aic26 = dev_get_drvdata(&spi->dev);
 
+       snd_soc_unregister_dai(&aic26_dai);
        kfree(aic26);
 
        return 0;
index cff276e..b47a749 100644 (file)
@@ -253,11 +253,17 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
 
        SOC_DOUBLE_R("Line DAC Playback Volume", DACL1_2_LLOPM_VOL,
                     DACR1_2_RLOPM_VOL, 0, 0x7f, 1),
-       SOC_DOUBLE_R("Line DAC Playback Switch", LLOPM_CTRL, RLOPM_CTRL, 3,
-                    0x01, 0),
-       SOC_DOUBLE_R("Line PGA Bypass Playback Volume", PGAL_2_LLOPM_VOL,
-                    PGAR_2_RLOPM_VOL, 0, 0x7f, 1),
-       SOC_DOUBLE_R("Line Line2 Bypass Playback Volume", LINE2L_2_LLOPM_VOL,
+       SOC_SINGLE("LineL Playback Switch", LLOPM_CTRL, 3, 0x01, 0),
+       SOC_SINGLE("LineR Playback Switch", RLOPM_CTRL, 3, 0x01, 0),
+       SOC_DOUBLE_R("LineL DAC Playback Volume", DACL1_2_LLOPM_VOL,
+                    DACR1_2_LLOPM_VOL, 0, 0x7f, 1),
+       SOC_SINGLE("LineL Left PGA Bypass Playback Volume", PGAL_2_LLOPM_VOL,
+                    0, 0x7f, 1),
+       SOC_SINGLE("LineR Right PGA Bypass Playback Volume", PGAR_2_RLOPM_VOL,
+                    0, 0x7f, 1),
+       SOC_DOUBLE_R("LineL Line2 Bypass Playback Volume", LINE2L_2_LLOPM_VOL,
+                    LINE2R_2_LLOPM_VOL, 0, 0x7f, 1),
+       SOC_DOUBLE_R("LineR Line2 Bypass Playback Volume", LINE2L_2_RLOPM_VOL,
                     LINE2R_2_RLOPM_VOL, 0, 0x7f, 1),
 
        SOC_DOUBLE_R("Mono DAC Playback Volume", DACL1_2_MONOLOPM_VOL,
@@ -272,8 +278,12 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
                     DACR1_2_HPROUT_VOL, 0, 0x7f, 1),
        SOC_DOUBLE_R("HP DAC Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3,
                     0x01, 0),
-       SOC_DOUBLE_R("HP PGA Bypass Playback Volume", PGAL_2_HPLOUT_VOL,
+       SOC_DOUBLE_R("HP Right PGA Bypass Playback Volume", PGAR_2_HPLOUT_VOL,
                     PGAR_2_HPROUT_VOL, 0, 0x7f, 1),
+       SOC_SINGLE("HPL PGA Bypass Playback Volume", PGAL_2_HPLOUT_VOL,
+                    0, 0x7f, 1),
+       SOC_SINGLE("HPR PGA Bypass Playback Volume", PGAL_2_HPROUT_VOL,
+                    0, 0x7f, 1),
        SOC_DOUBLE_R("HP Line2 Bypass Playback Volume", LINE2L_2_HPLOUT_VOL,
                     LINE2R_2_HPROUT_VOL, 0, 0x7f, 1),
 
@@ -281,8 +291,10 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
                     DACR1_2_HPRCOM_VOL, 0, 0x7f, 1),
        SOC_DOUBLE_R("HPCOM DAC Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3,
                     0x01, 0),
-       SOC_DOUBLE_R("HPCOM PGA Bypass Playback Volume", PGAL_2_HPLCOM_VOL,
-                    PGAR_2_HPRCOM_VOL, 0, 0x7f, 1),
+       SOC_SINGLE("HPLCOM PGA Bypass Playback Volume", PGAL_2_HPLCOM_VOL,
+                    0, 0x7f, 1),
+       SOC_SINGLE("HPRCOM PGA Bypass Playback Volume", PGAL_2_HPRCOM_VOL,
+                    0, 0x7f, 1),
        SOC_DOUBLE_R("HPCOM Line2 Bypass Playback Volume", LINE2L_2_HPLCOM_VOL,
                     LINE2R_2_HPRCOM_VOL, 0, 0x7f, 1),
 
@@ -333,7 +345,8 @@ SOC_DAPM_ENUM("Route", aic3x_enum[RHPCOM_ENUM]);
 
 /* Left DAC_L1 Mixer */
 static const struct snd_kcontrol_new aic3x_left_dac_mixer_controls[] = {
-       SOC_DAPM_SINGLE("Line Switch", DACL1_2_LLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("LineL Switch", DACL1_2_LLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("LineR Switch", DACL1_2_RLOPM_VOL, 7, 1, 0),
        SOC_DAPM_SINGLE("Mono Switch", DACL1_2_MONOLOPM_VOL, 7, 1, 0),
        SOC_DAPM_SINGLE("HP Switch", DACL1_2_HPLOUT_VOL, 7, 1, 0),
        SOC_DAPM_SINGLE("HPCOM Switch", DACL1_2_HPLCOM_VOL, 7, 1, 0),
@@ -341,7 +354,8 @@ static const struct snd_kcontrol_new aic3x_left_dac_mixer_controls[] = {
 
 /* Right DAC_R1 Mixer */
 static const struct snd_kcontrol_new aic3x_right_dac_mixer_controls[] = {
-       SOC_DAPM_SINGLE("Line Switch", DACR1_2_RLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("LineL Switch", DACR1_2_LLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("LineR Switch", DACR1_2_RLOPM_VOL, 7, 1, 0),
        SOC_DAPM_SINGLE("Mono Switch", DACR1_2_MONOLOPM_VOL, 7, 1, 0),
        SOC_DAPM_SINGLE("HP Switch", DACR1_2_HPROUT_VOL, 7, 1, 0),
        SOC_DAPM_SINGLE("HPCOM Switch", DACR1_2_HPRCOM_VOL, 7, 1, 0),
@@ -350,14 +364,18 @@ static const struct snd_kcontrol_new aic3x_right_dac_mixer_controls[] = {
 /* Left PGA Mixer */
 static const struct snd_kcontrol_new aic3x_left_pga_mixer_controls[] = {
        SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_LADC_CTRL, 3, 1, 1),
+       SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_LADC_CTRL, 3, 1, 1),
        SOC_DAPM_SINGLE_AIC3X("Line2L Switch", LINE2L_2_LADC_CTRL, 3, 1, 1),
        SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_LADC_CTRL, 4, 1, 1),
+       SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_LADC_CTRL, 0, 1, 1),
 };
 
 /* Right PGA Mixer */
 static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = {
        SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_RADC_CTRL, 3, 1, 1),
+       SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_RADC_CTRL, 3, 1, 1),
        SOC_DAPM_SINGLE_AIC3X("Line2R Switch", LINE2R_2_RADC_CTRL, 3, 1, 1),
+       SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_RADC_CTRL, 4, 1, 1),
        SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_RADC_CTRL, 0, 1, 1),
 };
 
@@ -379,34 +397,42 @@ SOC_DAPM_ENUM("Route", aic3x_enum[LINE2R_ENUM]);
 
 /* Left PGA Bypass Mixer */
 static const struct snd_kcontrol_new aic3x_left_pga_bp_mixer_controls[] = {
-       SOC_DAPM_SINGLE("Line Switch", PGAL_2_LLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("LineL Switch", PGAL_2_LLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("LineR Switch", PGAL_2_RLOPM_VOL, 7, 1, 0),
        SOC_DAPM_SINGLE("Mono Switch", PGAL_2_MONOLOPM_VOL, 7, 1, 0),
-       SOC_DAPM_SINGLE("HP Switch", PGAL_2_HPLOUT_VOL, 7, 1, 0),
-       SOC_DAPM_SINGLE("HPCOM Switch", PGAL_2_HPLCOM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HPL Switch", PGAL_2_HPLOUT_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HPR Switch", PGAL_2_HPROUT_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HPLCOM Switch", PGAL_2_HPLCOM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HPRCOM Switch", PGAL_2_HPRCOM_VOL, 7, 1, 0),
 };
 
 /* Right PGA Bypass Mixer */
 static const struct snd_kcontrol_new aic3x_right_pga_bp_mixer_controls[] = {
-       SOC_DAPM_SINGLE("Line Switch", PGAR_2_RLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("LineL Switch", PGAR_2_LLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("LineR Switch", PGAR_2_RLOPM_VOL, 7, 1, 0),
        SOC_DAPM_SINGLE("Mono Switch", PGAR_2_MONOLOPM_VOL, 7, 1, 0),
-       SOC_DAPM_SINGLE("HP Switch", PGAR_2_HPROUT_VOL, 7, 1, 0),
-       SOC_DAPM_SINGLE("HPCOM Switch", PGAR_2_HPRCOM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HPL Switch", PGAR_2_HPLOUT_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HPR Switch", PGAR_2_HPROUT_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HPLCOM Switch", PGAR_2_HPLCOM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HPRCOM Switch", PGAR_2_HPRCOM_VOL, 7, 1, 0),
 };
 
 /* Left Line2 Bypass Mixer */
 static const struct snd_kcontrol_new aic3x_left_line2_bp_mixer_controls[] = {
-       SOC_DAPM_SINGLE("Line Switch", LINE2L_2_LLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("LineL Switch", LINE2L_2_LLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("LineR Switch", LINE2L_2_RLOPM_VOL, 7, 1, 0),
        SOC_DAPM_SINGLE("Mono Switch", LINE2L_2_MONOLOPM_VOL, 7, 1, 0),
        SOC_DAPM_SINGLE("HP Switch", LINE2L_2_HPLOUT_VOL, 7, 1, 0),
-       SOC_DAPM_SINGLE("HPCOM Switch", LINE2L_2_HPLCOM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HPLCOM Switch", LINE2L_2_HPLCOM_VOL, 7, 1, 0),
 };
 
 /* Right Line2 Bypass Mixer */
 static const struct snd_kcontrol_new aic3x_right_line2_bp_mixer_controls[] = {
-       SOC_DAPM_SINGLE("Line Switch", LINE2R_2_RLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("LineL Switch", LINE2R_2_LLOPM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("LineR Switch", LINE2R_2_RLOPM_VOL, 7, 1, 0),
        SOC_DAPM_SINGLE("Mono Switch", LINE2R_2_MONOLOPM_VOL, 7, 1, 0),
        SOC_DAPM_SINGLE("HP Switch", LINE2R_2_HPROUT_VOL, 7, 1, 0),
-       SOC_DAPM_SINGLE("HPCOM Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0),
+       SOC_DAPM_SINGLE("HPRCOM Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0),
 };
 
 static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
@@ -439,22 +465,26 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
        /* Mono Output */
        SND_SOC_DAPM_PGA("Mono Out", MONOLOPM_CTRL, 0, 0, NULL, 0),
 
-       /* Left Inputs to Left ADC */
+       /* Inputs to Left ADC */
        SND_SOC_DAPM_ADC("Left ADC", "Left Capture", LINE1L_2_LADC_CTRL, 2, 0),
        SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0,
                           &aic3x_left_pga_mixer_controls[0],
                           ARRAY_SIZE(aic3x_left_pga_mixer_controls)),
        SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0,
                         &aic3x_left_line1_mux_controls),
+       SND_SOC_DAPM_MUX("Left Line1R Mux", SND_SOC_NOPM, 0, 0,
+                        &aic3x_left_line1_mux_controls),
        SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0,
                         &aic3x_left_line2_mux_controls),
 
-       /* Right Inputs to Right ADC */
+       /* Inputs to Right ADC */
        SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
                         LINE1R_2_RADC_CTRL, 2, 0),
        SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0,
                           &aic3x_right_pga_mixer_controls[0],
                           ARRAY_SIZE(aic3x_right_pga_mixer_controls)),
+       SND_SOC_DAPM_MUX("Right Line1L Mux", SND_SOC_NOPM, 0, 0,
+                        &aic3x_right_line1_mux_controls),
        SND_SOC_DAPM_MUX("Right Line1R Mux", SND_SOC_NOPM, 0, 0,
                         &aic3x_right_line1_mux_controls),
        SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,
@@ -531,7 +561,8 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Left DAC Mux", "DAC_L2", "Left DAC"},
        {"Left DAC Mux", "DAC_L3", "Left DAC"},
 
-       {"Left DAC_L1 Mixer", "Line Switch", "Left DAC Mux"},
+       {"Left DAC_L1 Mixer", "LineL Switch", "Left DAC Mux"},
+       {"Left DAC_L1 Mixer", "LineR Switch", "Left DAC Mux"},
        {"Left DAC_L1 Mixer", "Mono Switch", "Left DAC Mux"},
        {"Left DAC_L1 Mixer", "HP Switch", "Left DAC Mux"},
        {"Left DAC_L1 Mixer", "HPCOM Switch", "Left DAC Mux"},
@@ -557,7 +588,8 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Right DAC Mux", "DAC_R2", "Right DAC"},
        {"Right DAC Mux", "DAC_R3", "Right DAC"},
 
-       {"Right DAC_R1 Mixer", "Line Switch", "Right DAC Mux"},
+       {"Right DAC_R1 Mixer", "LineL Switch", "Right DAC Mux"},
+       {"Right DAC_R1 Mixer", "LineR Switch", "Right DAC Mux"},
        {"Right DAC_R1 Mixer", "Mono Switch", "Right DAC Mux"},
        {"Right DAC_R1 Mixer", "HP Switch", "Right DAC Mux"},
        {"Right DAC_R1 Mixer", "HPCOM Switch", "Right DAC Mux"},
@@ -592,8 +624,10 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Left Line2L Mux", "differential", "LINE2L"},
 
        {"Left PGA Mixer", "Line1L Switch", "Left Line1L Mux"},
+       {"Left PGA Mixer", "Line1R Switch", "Left Line1R Mux"},
        {"Left PGA Mixer", "Line2L Switch", "Left Line2L Mux"},
        {"Left PGA Mixer", "Mic3L Switch", "MIC3L"},
+       {"Left PGA Mixer", "Mic3R Switch", "MIC3R"},
 
        {"Left ADC", NULL, "Left PGA Mixer"},
        {"Left ADC", NULL, "GPIO1 dmic modclk"},
@@ -605,18 +639,23 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Right Line2R Mux", "single-ended", "LINE2R"},
        {"Right Line2R Mux", "differential", "LINE2R"},
 
+       {"Right PGA Mixer", "Line1L Switch", "Right Line1L Mux"},
        {"Right PGA Mixer", "Line1R Switch", "Right Line1R Mux"},
        {"Right PGA Mixer", "Line2R Switch", "Right Line2R Mux"},
+       {"Right PGA Mixer", "Mic3L Switch", "MIC3L"},
        {"Right PGA Mixer", "Mic3R Switch", "MIC3R"},
 
        {"Right ADC", NULL, "Right PGA Mixer"},
        {"Right ADC", NULL, "GPIO1 dmic modclk"},
 
        /* Left PGA Bypass */
-       {"Left PGA Bypass Mixer", "Line Switch", "Left PGA Mixer"},
+       {"Left PGA Bypass Mixer", "LineL Switch", "Left PGA Mixer"},
+       {"Left PGA Bypass Mixer", "LineR Switch", "Left PGA Mixer"},
        {"Left PGA Bypass Mixer", "Mono Switch", "Left PGA Mixer"},
-       {"Left PGA Bypass Mixer", "HP Switch", "Left PGA Mixer"},
-       {"Left PGA Bypass Mixer", "HPCOM Switch", "Left PGA Mixer"},
+       {"Left PGA Bypass Mixer", "HPL Switch", "Left PGA Mixer"},
+       {"Left PGA Bypass Mixer", "HPR Switch", "Left PGA Mixer"},
+       {"Left PGA Bypass Mixer", "HPLCOM Switch", "Left PGA Mixer"},
+       {"Left PGA Bypass Mixer", "HPRCOM Switch", "Left PGA Mixer"},
 
        {"Left HPCOM Mux", "differential of HPLOUT", "Left PGA Bypass Mixer"},
        {"Left HPCOM Mux", "constant VCM", "Left PGA Bypass Mixer"},
@@ -627,10 +666,13 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Left HP Out", NULL, "Left PGA Bypass Mixer"},
 
        /* Right PGA Bypass */
-       {"Right PGA Bypass Mixer", "Line Switch", "Right PGA Mixer"},
+       {"Right PGA Bypass Mixer", "LineL Switch", "Right PGA Mixer"},
+       {"Right PGA Bypass Mixer", "LineR Switch", "Right PGA Mixer"},
        {"Right PGA Bypass Mixer", "Mono Switch", "Right PGA Mixer"},
-       {"Right PGA Bypass Mixer", "HP Switch", "Right PGA Mixer"},
-       {"Right PGA Bypass Mixer", "HPCOM Switch", "Right PGA Mixer"},
+       {"Right PGA Bypass Mixer", "HPL Switch", "Right PGA Mixer"},
+       {"Right PGA Bypass Mixer", "HPR Switch", "Right PGA Mixer"},
+       {"Right PGA Bypass Mixer", "HPLCOM Switch", "Right PGA Mixer"},
+       {"Right PGA Bypass Mixer", "HPRCOM Switch", "Right PGA Mixer"},
 
        {"Right HPCOM Mux", "differential of HPROUT", "Right PGA Bypass Mixer"},
        {"Right HPCOM Mux", "constant VCM", "Right PGA Bypass Mixer"},
@@ -643,10 +685,11 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Right HP Out", NULL, "Right PGA Bypass Mixer"},
 
        /* Left Line2 Bypass */
-       {"Left Line2 Bypass Mixer", "Line Switch", "Left Line2L Mux"},
+       {"Left Line2 Bypass Mixer", "LineL Switch", "Left Line2L Mux"},
+       {"Left Line2 Bypass Mixer", "LineR Switch", "Left Line2L Mux"},
        {"Left Line2 Bypass Mixer", "Mono Switch", "Left Line2L Mux"},
        {"Left Line2 Bypass Mixer", "HP Switch", "Left Line2L Mux"},
-       {"Left Line2 Bypass Mixer", "HPCOM Switch", "Left Line2L Mux"},
+       {"Left Line2 Bypass Mixer", "HPLCOM Switch", "Left Line2L Mux"},
 
        {"Left HPCOM Mux", "differential of HPLOUT", "Left Line2 Bypass Mixer"},
        {"Left HPCOM Mux", "constant VCM", "Left Line2 Bypass Mixer"},
@@ -657,10 +700,11 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Left HP Out", NULL, "Left Line2 Bypass Mixer"},
 
        /* Right Line2 Bypass */
-       {"Right Line2 Bypass Mixer", "Line Switch", "Right Line2R Mux"},
+       {"Right Line2 Bypass Mixer", "LineL Switch", "Right Line2R Mux"},
+       {"Right Line2 Bypass Mixer", "LineR Switch", "Right Line2R Mux"},
        {"Right Line2 Bypass Mixer", "Mono Switch", "Right Line2R Mux"},
        {"Right Line2 Bypass Mixer", "HP Switch", "Right Line2R Mux"},
-       {"Right Line2 Bypass Mixer", "HPCOM Switch", "Right Line2R Mux"},
+       {"Right Line2 Bypass Mixer", "HPRCOM Switch", "Right Line2R Mux"},
 
        {"Right HPCOM Mux", "differential of HPROUT", "Right Line2 Bypass Mixer"},
        {"Right HPCOM Mux", "constant VCM", "Right Line2 Bypass Mixer"},
@@ -694,7 +738,8 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec)
 }
 
 static int aic3x_hw_params(struct snd_pcm_substream *substream,
-                          struct snd_pcm_hw_params *params)
+                          struct snd_pcm_hw_params *params,
+                          struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -846,6 +891,7 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
        struct snd_soc_codec *codec = codec_dai->codec;
        struct aic3x_priv *aic3x = codec->private_data;
        u8 iface_areg, iface_breg;
+       int delay = 0;
 
        iface_areg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
        iface_breg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
@@ -871,6 +917,8 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
                       SND_SOC_DAIFMT_INV_MASK)) {
        case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
                break;
+       case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
+               delay = 1;
        case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
                iface_breg |= (0x01 << 6);
                break;
@@ -887,6 +935,7 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
        /* set iface */
        aic3x_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg);
        aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg);
+       aic3x_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
 
        return 0;
 }
@@ -981,14 +1030,41 @@ int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio)
 }
 EXPORT_SYMBOL_GPL(aic3x_get_gpio);
 
+void aic3x_set_headset_detection(struct snd_soc_codec *codec, int detect,
+                                int headset_debounce, int button_debounce)
+{
+       u8 val;
+
+       val = ((detect & AIC3X_HEADSET_DETECT_MASK)
+               << AIC3X_HEADSET_DETECT_SHIFT) |
+             ((headset_debounce & AIC3X_HEADSET_DEBOUNCE_MASK)
+               << AIC3X_HEADSET_DEBOUNCE_SHIFT) |
+             ((button_debounce & AIC3X_BUTTON_DEBOUNCE_MASK)
+               << AIC3X_BUTTON_DEBOUNCE_SHIFT);
+
+       if (detect & AIC3X_HEADSET_DETECT_MASK)
+               val |= AIC3X_HEADSET_DETECT_ENABLED;
+
+       aic3x_write(codec, AIC3X_HEADSET_DETECT_CTRL_A, val);
+}
+EXPORT_SYMBOL_GPL(aic3x_set_headset_detection);
+
 int aic3x_headset_detected(struct snd_soc_codec *codec)
 {
        u8 val;
-       aic3x_read(codec, AIC3X_RT_IRQ_FLAGS_REG, &val);
-       return (val >> 2) & 1;
+       aic3x_read(codec, AIC3X_HEADSET_DETECT_CTRL_B, &val);
+       return (val >> 4) & 1;
 }
 EXPORT_SYMBOL_GPL(aic3x_headset_detected);
 
+int aic3x_button_pressed(struct snd_soc_codec *codec)
+{
+       u8 val;
+       aic3x_read(codec, AIC3X_HEADSET_DETECT_CTRL_B, &val);
+       return (val >> 5) & 1;
+}
+EXPORT_SYMBOL_GPL(aic3x_button_pressed);
+
 #define AIC3X_RATES    SNDRV_PCM_RATE_8000_96000
 #define AIC3X_FORMATS  (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
                         SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
@@ -1009,8 +1085,6 @@ struct snd_soc_dai aic3x_dai = {
                .formats = AIC3X_FORMATS,},
        .ops = {
                .hw_params = aic3x_hw_params,
-       },
-       .dai_ops = {
                .digital_mute = aic3x_mute,
                .set_sysclk = aic3x_set_dai_sysclk,
                .set_fmt = aic3x_set_dai_fmt,
@@ -1152,7 +1226,7 @@ static int aic3x_init(struct snd_soc_device *socdev)
 
        aic3x_add_controls(codec);
        aic3x_add_widgets(codec);
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                printk(KERN_ERR "aic3x: failed to register card\n");
                goto card_err;
@@ -1341,6 +1415,18 @@ struct snd_soc_codec_device soc_codec_dev_aic3x = {
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_aic3x);
 
+static int __init aic3x_modinit(void)
+{
+       return snd_soc_register_dai(&aic3x_dai);
+}
+module_init(aic3x_modinit);
+
+static void __exit aic3x_exit(void)
+{
+       snd_soc_unregister_dai(&aic3x_dai);
+}
+module_exit(aic3x_exit);
+
 MODULE_DESCRIPTION("ASoC TLV320AIC3X codec driver");
 MODULE_AUTHOR("Vladimir Barinov");
 MODULE_LICENSE("GPL");
index 00a195a..ac827e5 100644 (file)
 #define AIC3X_ASD_INTF_CTRLA           8
 /* Audio serial data interface control register B */
 #define AIC3X_ASD_INTF_CTRLB           9
+/* Audio serial data interface control register C */
+#define AIC3X_ASD_INTF_CTRLC           10
 /* Audio overflow status and PLL R value programming register */
 #define AIC3X_OVRF_STATUS_AND_PLLR_REG 11
 /* Audio codec digital filter control register */
 #define AIC3X_CODEC_DFILT_CTRL         12
-
+/* Headset/button press detection register */
+#define AIC3X_HEADSET_DETECT_CTRL_A    13
+#define AIC3X_HEADSET_DETECT_CTRL_B    14
 /* ADC PGA Gain control registers */
 #define LADC_VOL                       15
 #define RADC_VOL                       16
@@ -48,7 +52,9 @@
 #define MIC3LR_2_RADC_CTRL             18
 /* Line1 Input control registers */
 #define LINE1L_2_LADC_CTRL             19
+#define LINE1R_2_LADC_CTRL             21
 #define LINE1R_2_RADC_CTRL             22
+#define LINE1L_2_RADC_CTRL             24
 /* Line2 Input control registers */
 #define LINE2L_2_LADC_CTRL             20
 #define LINE2R_2_RADC_CTRL             23
@@ -79,6 +85,8 @@
 #define LINE2L_2_HPLOUT_VOL            45
 #define LINE2R_2_HPROUT_VOL            62
 #define PGAL_2_HPLOUT_VOL              46
+#define PGAL_2_HPROUT_VOL              60
+#define PGAR_2_HPLOUT_VOL              49
 #define PGAR_2_HPROUT_VOL              63
 #define DACL1_2_HPLOUT_VOL             47
 #define DACR1_2_HPROUT_VOL             64
@@ -88,6 +96,8 @@
 #define LINE2L_2_HPLCOM_VOL            52
 #define LINE2R_2_HPRCOM_VOL            69
 #define PGAL_2_HPLCOM_VOL              53
+#define PGAR_2_HPLCOM_VOL              56
+#define PGAL_2_HPRCOM_VOL              67
 #define PGAR_2_HPRCOM_VOL              70
 #define DACL1_2_HPLCOM_VOL             54
 #define DACR1_2_HPRCOM_VOL             71
 #define MONOLOPM_CTRL                  79
 /* Line Output Plus/Minus control registers */
 #define LINE2L_2_LLOPM_VOL             80
+#define LINE2L_2_RLOPM_VOL             87
+#define LINE2R_2_LLOPM_VOL             83
 #define LINE2R_2_RLOPM_VOL             90
 #define PGAL_2_LLOPM_VOL               81
+#define PGAL_2_RLOPM_VOL               88
+#define PGAR_2_LLOPM_VOL               84
 #define PGAR_2_RLOPM_VOL               91
 #define DACL1_2_LLOPM_VOL              82
+#define DACL1_2_RLOPM_VOL              89
 #define DACR1_2_RLOPM_VOL              92
+#define DACR1_2_LLOPM_VOL              85
 #define LLOPM_CTRL                     86
 #define RLOPM_CTRL                     93
 /* GPIO/IRQ registers */
@@ -221,7 +237,49 @@ enum {
 
 void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state);
 int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio);
+
+/* headset detection / button API */
+
+/* The AIC3x supports detection of stereo headsets (GND + left + right signal)
+ * and cellular headsets (GND + speaker output + microphone input).
+ * It is recommended to enable MIC bias for this function to work properly.
+ * For more information, please refer to the datasheet. */
+enum {
+       AIC3X_HEADSET_DETECT_OFF        = 0,
+       AIC3X_HEADSET_DETECT_STEREO     = 1,
+       AIC3X_HEADSET_DETECT_CELLULAR   = 2,
+       AIC3X_HEADSET_DETECT_BOTH       = 3
+};
+
+enum {
+       AIC3X_HEADSET_DEBOUNCE_16MS     = 0,
+       AIC3X_HEADSET_DEBOUNCE_32MS     = 1,
+       AIC3X_HEADSET_DEBOUNCE_64MS     = 2,
+       AIC3X_HEADSET_DEBOUNCE_128MS    = 3,
+       AIC3X_HEADSET_DEBOUNCE_256MS    = 4,
+       AIC3X_HEADSET_DEBOUNCE_512MS    = 5
+};
+
+enum {
+       AIC3X_BUTTON_DEBOUNCE_0MS       = 0,
+       AIC3X_BUTTON_DEBOUNCE_8MS       = 1,
+       AIC3X_BUTTON_DEBOUNCE_16MS      = 2,
+       AIC3X_BUTTON_DEBOUNCE_32MS      = 3
+};
+
+#define AIC3X_HEADSET_DETECT_ENABLED   0x80
+#define AIC3X_HEADSET_DETECT_SHIFT     5
+#define AIC3X_HEADSET_DETECT_MASK      3
+#define AIC3X_HEADSET_DEBOUNCE_SHIFT   2
+#define AIC3X_HEADSET_DEBOUNCE_MASK    7
+#define AIC3X_BUTTON_DEBOUNCE_SHIFT    0
+#define AIC3X_BUTTON_DEBOUNCE_MASK     3
+
+/* see the enums above for valid parameters to this function */
+void aic3x_set_headset_detection(struct snd_soc_codec *codec, int detect,
+                                int headset_debounce, int button_debounce);
 int aic3x_headset_detected(struct snd_soc_codec *codec);
+int aic3x_button_pressed(struct snd_soc_codec *codec);
 
 struct aic3x_setup_data {
        int i2c_bus;
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
new file mode 100644 (file)
index 0000000..5184888
--- /dev/null
@@ -0,0 +1,1317 @@
+/*
+ * ALSA SoC TWL4030 codec driver
+ *
+ * Author:      Steve Sakoman, <steve@sakoman.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "twl4030.h"
+
+/*
+ * twl4030 register cache & default register settings
+ */
+static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
+       0x00, /* this register not used         */
+       0x93, /* REG_CODEC_MODE         (0x1)   */
+       0xc3, /* REG_OPTION             (0x2)   */
+       0x00, /* REG_UNKNOWN            (0x3)   */
+       0x00, /* REG_MICBIAS_CTL        (0x4)   */
+       0x20, /* REG_ANAMICL            (0x5)   */
+       0x00, /* REG_ANAMICR            (0x6)   */
+       0x00, /* REG_AVADC_CTL          (0x7)   */
+       0x00, /* REG_ADCMICSEL          (0x8)   */
+       0x00, /* REG_DIGMIXING          (0x9)   */
+       0x0c, /* REG_ATXL1PGA           (0xA)   */
+       0x0c, /* REG_ATXR1PGA           (0xB)   */
+       0x00, /* REG_AVTXL2PGA          (0xC)   */
+       0x00, /* REG_AVTXR2PGA          (0xD)   */
+       0x01, /* REG_AUDIO_IF           (0xE)   */
+       0x00, /* REG_VOICE_IF           (0xF)   */
+       0x00, /* REG_ARXR1PGA           (0x10)  */
+       0x00, /* REG_ARXL1PGA           (0x11)  */
+       0x6c, /* REG_ARXR2PGA           (0x12)  */
+       0x6c, /* REG_ARXL2PGA           (0x13)  */
+       0x00, /* REG_VRXPGA             (0x14)  */
+       0x00, /* REG_VSTPGA             (0x15)  */
+       0x00, /* REG_VRX2ARXPGA         (0x16)  */
+       0x0c, /* REG_AVDAC_CTL          (0x17)  */
+       0x00, /* REG_ARX2VTXPGA         (0x18)  */
+       0x00, /* REG_ARXL1_APGA_CTL     (0x19)  */
+       0x00, /* REG_ARXR1_APGA_CTL     (0x1A)  */
+       0x4b, /* REG_ARXL2_APGA_CTL     (0x1B)  */
+       0x4b, /* REG_ARXR2_APGA_CTL     (0x1C)  */
+       0x00, /* REG_ATX2ARXPGA         (0x1D)  */
+       0x00, /* REG_BT_IF              (0x1E)  */
+       0x00, /* REG_BTPGA              (0x1F)  */
+       0x00, /* REG_BTSTPGA            (0x20)  */
+       0x00, /* REG_EAR_CTL            (0x21)  */
+       0x24, /* REG_HS_SEL             (0x22)  */
+       0x0a, /* REG_HS_GAIN_SET        (0x23)  */
+       0x00, /* REG_HS_POPN_SET        (0x24)  */
+       0x00, /* REG_PREDL_CTL          (0x25)  */
+       0x00, /* REG_PREDR_CTL          (0x26)  */
+       0x00, /* REG_PRECKL_CTL         (0x27)  */
+       0x00, /* REG_PRECKR_CTL         (0x28)  */
+       0x00, /* REG_HFL_CTL            (0x29)  */
+       0x00, /* REG_HFR_CTL            (0x2A)  */
+       0x00, /* REG_ALC_CTL            (0x2B)  */
+       0x00, /* REG_ALC_SET1           (0x2C)  */
+       0x00, /* REG_ALC_SET2           (0x2D)  */
+       0x00, /* REG_BOOST_CTL          (0x2E)  */
+       0x00, /* REG_SOFTVOL_CTL        (0x2F)  */
+       0x00, /* REG_DTMF_FREQSEL       (0x30)  */
+       0x00, /* REG_DTMF_TONEXT1H      (0x31)  */
+       0x00, /* REG_DTMF_TONEXT1L      (0x32)  */
+       0x00, /* REG_DTMF_TONEXT2H      (0x33)  */
+       0x00, /* REG_DTMF_TONEXT2L      (0x34)  */
+       0x00, /* REG_DTMF_TONOFF        (0x35)  */
+       0x00, /* REG_DTMF_WANONOFF      (0x36)  */
+       0x00, /* REG_I2S_RX_SCRAMBLE_H  (0x37)  */
+       0x00, /* REG_I2S_RX_SCRAMBLE_M  (0x38)  */
+       0x00, /* REG_I2S_RX_SCRAMBLE_L  (0x39)  */
+       0x16, /* REG_APLL_CTL           (0x3A)  */
+       0x00, /* REG_DTMF_CTL           (0x3B)  */
+       0x00, /* REG_DTMF_PGA_CTL2      (0x3C)  */
+       0x00, /* REG_DTMF_PGA_CTL1      (0x3D)  */
+       0x00, /* REG_MISC_SET_1         (0x3E)  */
+       0x00, /* REG_PCMBTMUX           (0x3F)  */
+       0x00, /* not used               (0x40)  */
+       0x00, /* not used               (0x41)  */
+       0x00, /* not used               (0x42)  */
+       0x00, /* REG_RX_PATH_SEL        (0x43)  */
+       0x00, /* REG_VDL_APGA_CTL       (0x44)  */
+       0x00, /* REG_VIBRA_CTL          (0x45)  */
+       0x00, /* REG_VIBRA_SET          (0x46)  */
+       0x00, /* REG_VIBRA_PWM_SET      (0x47)  */
+       0x00, /* REG_ANAMIC_GAIN        (0x48)  */
+       0x00, /* REG_MISC_SET_2         (0x49)  */
+};
+
+/*
+ * read twl4030 register cache
+ */
+static inline unsigned int twl4030_read_reg_cache(struct snd_soc_codec *codec,
+       unsigned int reg)
+{
+       u8 *cache = codec->reg_cache;
+
+       return cache[reg];
+}
+
+/*
+ * write twl4030 register cache
+ */
+static inline void twl4030_write_reg_cache(struct snd_soc_codec *codec,
+                                               u8 reg, u8 value)
+{
+       u8 *cache = codec->reg_cache;
+
+       if (reg >= TWL4030_CACHEREGNUM)
+               return;
+       cache[reg] = value;
+}
+
+/*
+ * write to the twl4030 register space
+ */
+static int twl4030_write(struct snd_soc_codec *codec,
+                       unsigned int reg, unsigned int value)
+{
+       twl4030_write_reg_cache(codec, reg, value);
+       return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg);
+}
+
+static void twl4030_clear_codecpdz(struct snd_soc_codec *codec)
+{
+       u8 mode;
+
+       mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
+       twl4030_write(codec, TWL4030_REG_CODEC_MODE,
+               mode & ~TWL4030_CODECPDZ);
+
+       /* REVISIT: this delay is present in TI sample drivers */
+       /* but there seems to be no TRM requirement for it     */
+       udelay(10);
+}
+
+static void twl4030_set_codecpdz(struct snd_soc_codec *codec)
+{
+       u8 mode;
+
+       mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
+       twl4030_write(codec, TWL4030_REG_CODEC_MODE,
+               mode | TWL4030_CODECPDZ);
+
+       /* REVISIT: this delay is present in TI sample drivers */
+       /* but there seems to be no TRM requirement for it     */
+       udelay(10);
+}
+
+static void twl4030_init_chip(struct snd_soc_codec *codec)
+{
+       int i;
+
+       /* clear CODECPDZ prior to setting register defaults */
+       twl4030_clear_codecpdz(codec);
+
+       /* set all audio section registers to reasonable defaults */
+       for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++)
+               twl4030_write(codec, i, twl4030_reg[i]);
+
+}
+
+/* Earpiece */
+static const char *twl4030_earpiece_texts[] =
+               {"Off", "DACL1", "DACL2", "Invalid", "DACR1"};
+
+static const struct soc_enum twl4030_earpiece_enum =
+       SOC_ENUM_SINGLE(TWL4030_REG_EAR_CTL, 1,
+                       ARRAY_SIZE(twl4030_earpiece_texts),
+                       twl4030_earpiece_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_earpiece_control =
+SOC_DAPM_ENUM("Route", twl4030_earpiece_enum);
+
+/* PreDrive Left */
+static const char *twl4030_predrivel_texts[] =
+               {"Off", "DACL1", "DACL2", "Invalid", "DACR2"};
+
+static const struct soc_enum twl4030_predrivel_enum =
+       SOC_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 1,
+                       ARRAY_SIZE(twl4030_predrivel_texts),
+                       twl4030_predrivel_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_predrivel_control =
+SOC_DAPM_ENUM("Route", twl4030_predrivel_enum);
+
+/* PreDrive Right */
+static const char *twl4030_predriver_texts[] =
+               {"Off", "DACR1", "DACR2", "Invalid", "DACL2"};
+
+static const struct soc_enum twl4030_predriver_enum =
+       SOC_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 1,
+                       ARRAY_SIZE(twl4030_predriver_texts),
+                       twl4030_predriver_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_predriver_control =
+SOC_DAPM_ENUM("Route", twl4030_predriver_enum);
+
+/* Headset Left */
+static const char *twl4030_hsol_texts[] =
+               {"Off", "DACL1", "DACL2"};
+
+static const struct soc_enum twl4030_hsol_enum =
+       SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 1,
+                       ARRAY_SIZE(twl4030_hsol_texts),
+                       twl4030_hsol_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_hsol_control =
+SOC_DAPM_ENUM("Route", twl4030_hsol_enum);
+
+/* Headset Right */
+static const char *twl4030_hsor_texts[] =
+               {"Off", "DACR1", "DACR2"};
+
+static const struct soc_enum twl4030_hsor_enum =
+       SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 4,
+                       ARRAY_SIZE(twl4030_hsor_texts),
+                       twl4030_hsor_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_hsor_control =
+SOC_DAPM_ENUM("Route", twl4030_hsor_enum);
+
+/* Carkit Left */
+static const char *twl4030_carkitl_texts[] =
+               {"Off", "DACL1", "DACL2"};
+
+static const struct soc_enum twl4030_carkitl_enum =
+       SOC_ENUM_SINGLE(TWL4030_REG_PRECKL_CTL, 1,
+                       ARRAY_SIZE(twl4030_carkitl_texts),
+                       twl4030_carkitl_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_carkitl_control =
+SOC_DAPM_ENUM("Route", twl4030_carkitl_enum);
+
+/* Carkit Right */
+static const char *twl4030_carkitr_texts[] =
+               {"Off", "DACR1", "DACR2"};
+
+static const struct soc_enum twl4030_carkitr_enum =
+       SOC_ENUM_SINGLE(TWL4030_REG_PRECKR_CTL, 1,
+                       ARRAY_SIZE(twl4030_carkitr_texts),
+                       twl4030_carkitr_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_carkitr_control =
+SOC_DAPM_ENUM("Route", twl4030_carkitr_enum);
+
+/* Handsfree Left */
+static const char *twl4030_handsfreel_texts[] =
+               {"Voice", "DACL1", "DACL2", "DACR2"};
+
+static const struct soc_enum twl4030_handsfreel_enum =
+       SOC_ENUM_SINGLE(TWL4030_REG_HFL_CTL, 0,
+                       ARRAY_SIZE(twl4030_handsfreel_texts),
+                       twl4030_handsfreel_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_handsfreel_control =
+SOC_DAPM_ENUM("Route", twl4030_handsfreel_enum);
+
+/* Handsfree Right */
+static const char *twl4030_handsfreer_texts[] =
+               {"Voice", "DACR1", "DACR2", "DACL2"};
+
+static const struct soc_enum twl4030_handsfreer_enum =
+       SOC_ENUM_SINGLE(TWL4030_REG_HFR_CTL, 0,
+                       ARRAY_SIZE(twl4030_handsfreer_texts),
+                       twl4030_handsfreer_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_handsfreer_control =
+SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum);
+
+static int outmixer_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       int ret = 0;
+       int val;
+
+       switch (e->reg) {
+       case TWL4030_REG_PREDL_CTL:
+       case TWL4030_REG_PREDR_CTL:
+       case TWL4030_REG_EAR_CTL:
+               val = w->value >> e->shift_l;
+               if (val == 3) {
+                       printk(KERN_WARNING
+                       "Invalid MUX setting for register 0x%02x (%d)\n",
+                             e->reg, val);
+                       ret = -1;
+               }
+               break;
+       }
+
+       return ret;
+}
+
+static int handsfree_event(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct soc_enum *e = (struct soc_enum *)w->kcontrols->private_value;
+       unsigned char hs_ctl;
+
+       hs_ctl = twl4030_read_reg_cache(w->codec, e->reg);
+
+       if (hs_ctl & TWL4030_HF_CTL_REF_EN) {
+               hs_ctl |= TWL4030_HF_CTL_RAMP_EN;
+               twl4030_write(w->codec, e->reg, hs_ctl);
+               hs_ctl |= TWL4030_HF_CTL_LOOP_EN;
+               twl4030_write(w->codec, e->reg, hs_ctl);
+               hs_ctl |= TWL4030_HF_CTL_HB_EN;
+               twl4030_write(w->codec, e->reg, hs_ctl);
+       } else {
+               hs_ctl &= ~(TWL4030_HF_CTL_RAMP_EN | TWL4030_HF_CTL_LOOP_EN
+                               | TWL4030_HF_CTL_HB_EN);
+               twl4030_write(w->codec, e->reg, hs_ctl);
+       }
+
+       return 0;
+}
+
+/*
+ * Some of the gain controls in TWL (mostly those which are associated with
+ * the outputs) are implemented in an interesting way:
+ * 0x0 : Power down (mute)
+ * 0x1 : 6dB
+ * 0x2 : 0 dB
+ * 0x3 : -6 dB
+ * Inverting not going to help with these.
+ * Custom volsw and volsw_2r get/put functions to handle these gain bits.
+ */
+#define SOC_DOUBLE_TLV_TWL4030(xname, xreg, shift_left, shift_right, xmax,\
+                              xinvert, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw, \
+       .get = snd_soc_get_volsw_twl4030, \
+       .put = snd_soc_put_volsw_twl4030, \
+       .private_value = (unsigned long)&(struct soc_mixer_control) \
+               {.reg = xreg, .shift = shift_left, .rshift = shift_right,\
+                .max = xmax, .invert = xinvert} }
+#define SOC_DOUBLE_R_TLV_TWL4030(xname, reg_left, reg_right, xshift, xmax,\
+                                xinvert, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw_2r, \
+       .get = snd_soc_get_volsw_r2_twl4030,\
+       .put = snd_soc_put_volsw_r2_twl4030, \
+       .private_value = (unsigned long)&(struct soc_mixer_control) \
+               {.reg = reg_left, .rreg = reg_right, .shift = xshift, \
+                .rshift = xshift, .max = xmax, .invert = xinvert} }
+#define SOC_SINGLE_TLV_TWL4030(xname, xreg, xshift, xmax, xinvert, tlv_array) \
+       SOC_DOUBLE_TLV_TWL4030(xname, xreg, xshift, xshift, xmax, \
+                              xinvert, tlv_array)
+
+static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       unsigned int reg = mc->reg;
+       unsigned int shift = mc->shift;
+       unsigned int rshift = mc->rshift;
+       int max = mc->max;
+       int mask = (1 << fls(max)) - 1;
+
+       ucontrol->value.integer.value[0] =
+               (snd_soc_read(codec, reg) >> shift) & mask;
+       if (ucontrol->value.integer.value[0])
+               ucontrol->value.integer.value[0] =
+                       max + 1 - ucontrol->value.integer.value[0];
+
+       if (shift != rshift) {
+               ucontrol->value.integer.value[1] =
+                       (snd_soc_read(codec, reg) >> rshift) & mask;
+               if (ucontrol->value.integer.value[1])
+                       ucontrol->value.integer.value[1] =
+                               max + 1 - ucontrol->value.integer.value[1];
+       }
+
+       return 0;
+}
+
+static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       unsigned int reg = mc->reg;
+       unsigned int shift = mc->shift;
+       unsigned int rshift = mc->rshift;
+       int max = mc->max;
+       int mask = (1 << fls(max)) - 1;
+       unsigned short val, val2, val_mask;
+
+       val = (ucontrol->value.integer.value[0] & mask);
+
+       val_mask = mask << shift;
+       if (val)
+               val = max + 1 - val;
+       val = val << shift;
+       if (shift != rshift) {
+               val2 = (ucontrol->value.integer.value[1] & mask);
+               val_mask |= mask << rshift;
+               if (val2)
+                       val2 = max + 1 - val2;
+               val |= val2 << rshift;
+       }
+       return snd_soc_update_bits(codec, reg, val_mask, val);
+}
+
+static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
+       unsigned int shift = mc->shift;
+       int max = mc->max;
+       int mask = (1<<fls(max))-1;
+
+       ucontrol->value.integer.value[0] =
+               (snd_soc_read(codec, reg) >> shift) & mask;
+       ucontrol->value.integer.value[1] =
+               (snd_soc_read(codec, reg2) >> shift) & mask;
+
+       if (ucontrol->value.integer.value[0])
+               ucontrol->value.integer.value[0] =
+                       max + 1 - ucontrol->value.integer.value[0];
+       if (ucontrol->value.integer.value[1])
+               ucontrol->value.integer.value[1] =
+                       max + 1 - ucontrol->value.integer.value[1];
+
+       return 0;
+}
+
+static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
+       unsigned int shift = mc->shift;
+       int max = mc->max;
+       int mask = (1 << fls(max)) - 1;
+       int err;
+       unsigned short val, val2, val_mask;
+
+       val_mask = mask << shift;
+       val = (ucontrol->value.integer.value[0] & mask);
+       val2 = (ucontrol->value.integer.value[1] & mask);
+
+       if (val)
+               val = max + 1 - val;
+       if (val2)
+               val2 = max + 1 - val2;
+
+       val = val << shift;
+       val2 = val2 << shift;
+
+       err = snd_soc_update_bits(codec, reg, val_mask, val);
+       if (err < 0)
+               return err;
+
+       err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+       return err;
+}
+
+static int twl4030_get_left_input(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = kcontrol->private_data;
+       u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
+       int result = 0;
+
+       /* one bit must be set a time */
+       reg &= TWL4030_CKMIC_EN | TWL4030_AUXL_EN | TWL4030_HSMIC_EN
+                       | TWL4030_MAINMIC_EN;
+       if (reg != 0) {
+               result++;
+               while ((reg & 1) == 0) {
+                       result++;
+                       reg >>= 1;
+               }
+       }
+
+       ucontrol->value.integer.value[0] = result;
+       return 0;
+}
+
+static int twl4030_put_left_input(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = kcontrol->private_data;
+       int value = ucontrol->value.integer.value[0];
+       u8 anamicl, micbias, avadc_ctl;
+
+       anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
+       anamicl &= ~(TWL4030_CKMIC_EN | TWL4030_AUXL_EN | TWL4030_HSMIC_EN
+                       | TWL4030_MAINMIC_EN);
+       micbias = twl4030_read_reg_cache(codec, TWL4030_REG_MICBIAS_CTL);
+       micbias &= ~(TWL4030_HSMICBIAS_EN | TWL4030_MICBIAS1_EN);
+       avadc_ctl = twl4030_read_reg_cache(codec, TWL4030_REG_AVADC_CTL);
+
+       switch (value) {
+       case 1:
+               anamicl |= TWL4030_MAINMIC_EN;
+               micbias |= TWL4030_MICBIAS1_EN;
+               break;
+       case 2:
+               anamicl |= TWL4030_HSMIC_EN;
+               micbias |= TWL4030_HSMICBIAS_EN;
+               break;
+       case 3:
+               anamicl |= TWL4030_AUXL_EN;
+               break;
+       case 4:
+               anamicl |= TWL4030_CKMIC_EN;
+               break;
+       default:
+               break;
+       }
+
+       /* If some input is selected, enable amp and ADC */
+       if (value != 0) {
+               anamicl |= TWL4030_MICAMPL_EN;
+               avadc_ctl |= TWL4030_ADCL_EN;
+       } else {
+               anamicl &= ~TWL4030_MICAMPL_EN;
+               avadc_ctl &= ~TWL4030_ADCL_EN;
+       }
+
+       twl4030_write(codec, TWL4030_REG_ANAMICL, anamicl);
+       twl4030_write(codec, TWL4030_REG_MICBIAS_CTL, micbias);
+       twl4030_write(codec, TWL4030_REG_AVADC_CTL, avadc_ctl);
+
+       return 1;
+}
+
+static int twl4030_get_right_input(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = kcontrol->private_data;
+       u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICR);
+       int value = 0;
+
+       reg &= TWL4030_SUBMIC_EN|TWL4030_AUXR_EN;
+       switch (reg) {
+       case TWL4030_SUBMIC_EN:
+               value = 1;
+               break;
+       case TWL4030_AUXR_EN:
+               value = 2;
+               break;
+       default:
+               break;
+       }
+
+       ucontrol->value.integer.value[0] = value;
+       return 0;
+}
+
+static int twl4030_put_right_input(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = kcontrol->private_data;
+       int value = ucontrol->value.integer.value[0];
+       u8 anamicr, micbias, avadc_ctl;
+
+       anamicr = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICR);
+       anamicr &= ~(TWL4030_SUBMIC_EN|TWL4030_AUXR_EN);
+       micbias = twl4030_read_reg_cache(codec, TWL4030_REG_MICBIAS_CTL);
+       micbias &= ~TWL4030_MICBIAS2_EN;
+       avadc_ctl = twl4030_read_reg_cache(codec, TWL4030_REG_AVADC_CTL);
+
+       switch (value) {
+       case 1:
+               anamicr |= TWL4030_SUBMIC_EN;
+               micbias |= TWL4030_MICBIAS2_EN;
+               break;
+       case 2:
+               anamicr |= TWL4030_AUXR_EN;
+               break;
+       default:
+               break;
+       }
+
+       if (value != 0) {
+               anamicr |= TWL4030_MICAMPR_EN;
+               avadc_ctl |= TWL4030_ADCR_EN;
+       } else {
+               anamicr &= ~TWL4030_MICAMPR_EN;
+               avadc_ctl &= ~TWL4030_ADCR_EN;
+       }
+
+       twl4030_write(codec, TWL4030_REG_ANAMICR, anamicr);
+       twl4030_write(codec, TWL4030_REG_MICBIAS_CTL, micbias);
+       twl4030_write(codec, TWL4030_REG_AVADC_CTL, avadc_ctl);
+
+       return 1;
+}
+
+static const char *twl4030_left_in_sel[] = {
+       "None",
+       "Main Mic",
+       "Headset Mic",
+       "Line In",
+       "Carkit Mic",
+};
+
+static const char *twl4030_right_in_sel[] = {
+       "None",
+       "Sub Mic",
+       "Line In",
+};
+
+static const struct soc_enum twl4030_left_input_mux =
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl4030_left_in_sel),
+               twl4030_left_in_sel);
+
+static const struct soc_enum twl4030_right_input_mux =
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl4030_right_in_sel),
+               twl4030_right_in_sel);
+
+/*
+ * FGAIN volume control:
+ * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB)
+ */
+static DECLARE_TLV_DB_SCALE(digital_fine_tlv, -6300, 100, 1);
+
+/*
+ * CGAIN volume control:
+ * 0 dB to 12 dB in 6 dB steps
+ * value 2 and 3 means 12 dB
+ */
+static DECLARE_TLV_DB_SCALE(digital_coarse_tlv, 0, 600, 0);
+
+/*
+ * Analog playback gain
+ * -24 dB to 12 dB in 2 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(analog_tlv, -2400, 200, 0);
+
+/*
+ * Gain controls tied to outputs
+ * -6 dB to 6 dB in 6 dB steps (mute instead of -12)
+ */
+static DECLARE_TLV_DB_SCALE(output_tvl, -1200, 600, 1);
+
+/*
+ * Capture gain after the ADCs
+ * from 0 dB to 31 dB in 1 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0);
+
+/*
+ * Gain control for input amplifiers
+ * 0 dB to 30 dB in 6 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new twl4030_snd_controls[] = {
+       /* Common playback gain controls */
+       SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume",
+               TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA,
+               0, 0x3f, 0, digital_fine_tlv),
+       SOC_DOUBLE_R_TLV("DAC2 Digital Fine Playback Volume",
+               TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA,
+               0, 0x3f, 0, digital_fine_tlv),
+
+       SOC_DOUBLE_R_TLV("DAC1 Digital Coarse Playback Volume",
+               TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA,
+               6, 0x2, 0, digital_coarse_tlv),
+       SOC_DOUBLE_R_TLV("DAC2 Digital Coarse Playback Volume",
+               TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA,
+               6, 0x2, 0, digital_coarse_tlv),
+
+       SOC_DOUBLE_R_TLV("DAC1 Analog Playback Volume",
+               TWL4030_REG_ARXL1_APGA_CTL, TWL4030_REG_ARXR1_APGA_CTL,
+               3, 0x12, 1, analog_tlv),
+       SOC_DOUBLE_R_TLV("DAC2 Analog Playback Volume",
+               TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL,
+               3, 0x12, 1, analog_tlv),
+       SOC_DOUBLE_R("DAC1 Analog Playback Switch",
+               TWL4030_REG_ARXL1_APGA_CTL, TWL4030_REG_ARXR1_APGA_CTL,
+               1, 1, 0),
+       SOC_DOUBLE_R("DAC2 Analog Playback Switch",
+               TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL,
+               1, 1, 0),
+
+       /* Separate output gain controls */
+       SOC_DOUBLE_R_TLV_TWL4030("PreDriv Playback Volume",
+               TWL4030_REG_PREDL_CTL, TWL4030_REG_PREDR_CTL,
+               4, 3, 0, output_tvl),
+
+       SOC_DOUBLE_TLV_TWL4030("Headset Playback Volume",
+               TWL4030_REG_HS_GAIN_SET, 0, 2, 3, 0, output_tvl),
+
+       SOC_DOUBLE_R_TLV_TWL4030("Carkit Playback Volume",
+               TWL4030_REG_PRECKL_CTL, TWL4030_REG_PRECKR_CTL,
+               4, 3, 0, output_tvl),
+
+       SOC_SINGLE_TLV_TWL4030("Earpiece Playback Volume",
+               TWL4030_REG_EAR_CTL, 4, 3, 0, output_tvl),
+
+       /* Common capture gain controls */
+       SOC_DOUBLE_R_TLV("Capture Volume",
+               TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA,
+               0, 0x1f, 0, digital_capture_tlv),
+
+       SOC_DOUBLE_TLV("Input Boost Volume", TWL4030_REG_ANAMIC_GAIN,
+               0, 3, 5, 0, input_gain_tlv),
+
+       /* Input source controls */
+       SOC_ENUM_EXT("Left Input Source", twl4030_left_input_mux,
+               twl4030_get_left_input, twl4030_put_left_input),
+       SOC_ENUM_EXT("Right Input Source", twl4030_right_input_mux,
+               twl4030_get_right_input, twl4030_put_right_input),
+};
+
+/* add non dapm controls */
+static int twl4030_add_controls(struct snd_soc_codec *codec)
+{
+       int err, i;
+
+       for (i = 0; i < ARRAY_SIZE(twl4030_snd_controls); i++) {
+               err = snd_ctl_add(codec->card,
+                                 snd_soc_cnew(&twl4030_snd_controls[i],
+                                               codec, NULL));
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
+       SND_SOC_DAPM_INPUT("INL"),
+       SND_SOC_DAPM_INPUT("INR"),
+
+       SND_SOC_DAPM_OUTPUT("OUTL"),
+       SND_SOC_DAPM_OUTPUT("OUTR"),
+       SND_SOC_DAPM_OUTPUT("EARPIECE"),
+       SND_SOC_DAPM_OUTPUT("PREDRIVEL"),
+       SND_SOC_DAPM_OUTPUT("PREDRIVER"),
+       SND_SOC_DAPM_OUTPUT("HSOL"),
+       SND_SOC_DAPM_OUTPUT("HSOR"),
+       SND_SOC_DAPM_OUTPUT("CARKITL"),
+       SND_SOC_DAPM_OUTPUT("CARKITR"),
+       SND_SOC_DAPM_OUTPUT("HFL"),
+       SND_SOC_DAPM_OUTPUT("HFR"),
+
+       /* DACs */
+       SND_SOC_DAPM_DAC("DAC Right1", "Right Front Playback",
+                       TWL4030_REG_AVDAC_CTL, 0, 0),
+       SND_SOC_DAPM_DAC("DAC Left1", "Left Front Playback",
+                       TWL4030_REG_AVDAC_CTL, 1, 0),
+       SND_SOC_DAPM_DAC("DAC Right2", "Right Rear Playback",
+                       TWL4030_REG_AVDAC_CTL, 2, 0),
+       SND_SOC_DAPM_DAC("DAC Left2", "Left Rear Playback",
+                       TWL4030_REG_AVDAC_CTL, 3, 0),
+
+       /* Analog PGAs */
+       SND_SOC_DAPM_PGA("ARXR1_APGA", TWL4030_REG_ARXR1_APGA_CTL,
+                       0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("ARXL1_APGA", TWL4030_REG_ARXL1_APGA_CTL,
+                       0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("ARXR2_APGA", TWL4030_REG_ARXR2_APGA_CTL,
+                       0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("ARXL2_APGA", TWL4030_REG_ARXL2_APGA_CTL,
+                       0, 0, NULL, 0),
+
+       /* Output MUX controls */
+       /* Earpiece */
+       SND_SOC_DAPM_MUX_E("Earpiece Mux", SND_SOC_NOPM, 0, 0,
+               &twl4030_dapm_earpiece_control, outmixer_event,
+               SND_SOC_DAPM_PRE_REG),
+       /* PreDrivL/R */
+       SND_SOC_DAPM_MUX_E("PredriveL Mux", SND_SOC_NOPM, 0, 0,
+               &twl4030_dapm_predrivel_control, outmixer_event,
+               SND_SOC_DAPM_PRE_REG),
+       SND_SOC_DAPM_MUX_E("PredriveR Mux", SND_SOC_NOPM, 0, 0,
+               &twl4030_dapm_predriver_control, outmixer_event,
+               SND_SOC_DAPM_PRE_REG),
+       /* HeadsetL/R */
+       SND_SOC_DAPM_MUX("HeadsetL Mux", SND_SOC_NOPM, 0, 0,
+               &twl4030_dapm_hsol_control),
+       SND_SOC_DAPM_MUX("HeadsetR Mux", SND_SOC_NOPM, 0, 0,
+               &twl4030_dapm_hsor_control),
+       /* CarkitL/R */
+       SND_SOC_DAPM_MUX("CarkitL Mux", SND_SOC_NOPM, 0, 0,
+               &twl4030_dapm_carkitl_control),
+       SND_SOC_DAPM_MUX("CarkitR Mux", SND_SOC_NOPM, 0, 0,
+               &twl4030_dapm_carkitr_control),
+       /* HandsfreeL/R */
+       SND_SOC_DAPM_MUX_E("HandsfreeL Mux", TWL4030_REG_HFL_CTL, 5, 0,
+               &twl4030_dapm_handsfreel_control, handsfree_event,
+               SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MUX_E("HandsfreeR Mux", TWL4030_REG_HFR_CTL, 5, 0,
+               &twl4030_dapm_handsfreer_control, handsfree_event,
+               SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_ADC("ADCL", "Left Capture", SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_ADC("ADCR", "Right Capture", SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+       {"ARXL1_APGA", NULL, "DAC Left1"},
+       {"ARXR1_APGA", NULL, "DAC Right1"},
+       {"ARXL2_APGA", NULL, "DAC Left2"},
+       {"ARXR2_APGA", NULL, "DAC Right2"},
+
+       /* Internal playback routings */
+       /* Earpiece */
+       {"Earpiece Mux", "DACL1", "ARXL1_APGA"},
+       {"Earpiece Mux", "DACL2", "ARXL2_APGA"},
+       {"Earpiece Mux", "DACR1", "ARXR1_APGA"},
+       /* PreDrivL */
+       {"PredriveL Mux", "DACL1", "ARXL1_APGA"},
+       {"PredriveL Mux", "DACL2", "ARXL2_APGA"},
+       {"PredriveL Mux", "DACR2", "ARXR2_APGA"},
+       /* PreDrivR */
+       {"PredriveR Mux", "DACR1", "ARXR1_APGA"},
+       {"PredriveR Mux", "DACR2", "ARXR2_APGA"},
+       {"PredriveR Mux", "DACL2", "ARXL2_APGA"},
+       /* HeadsetL */
+       {"HeadsetL Mux", "DACL1", "ARXL1_APGA"},
+       {"HeadsetL Mux", "DACL2", "ARXL2_APGA"},
+       /* HeadsetR */
+       {"HeadsetR Mux", "DACR1", "ARXR1_APGA"},
+       {"HeadsetR Mux", "DACR2", "ARXR2_APGA"},
+       /* CarkitL */
+       {"CarkitL Mux", "DACL1", "ARXL1_APGA"},
+       {"CarkitL Mux", "DACL2", "ARXL2_APGA"},
+       /* CarkitR */
+       {"CarkitR Mux", "DACR1", "ARXR1_APGA"},
+       {"CarkitR Mux", "DACR2", "ARXR2_APGA"},
+       /* HandsfreeL */
+       {"HandsfreeL Mux", "DACL1", "ARXL1_APGA"},
+       {"HandsfreeL Mux", "DACL2", "ARXL2_APGA"},
+       {"HandsfreeL Mux", "DACR2", "ARXR2_APGA"},
+       /* HandsfreeR */
+       {"HandsfreeR Mux", "DACR1", "ARXR1_APGA"},
+       {"HandsfreeR Mux", "DACR2", "ARXR2_APGA"},
+       {"HandsfreeR Mux", "DACL2", "ARXL2_APGA"},
+
+       /* outputs */
+       {"OUTL", NULL, "ARXL2_APGA"},
+       {"OUTR", NULL, "ARXR2_APGA"},
+       {"EARPIECE", NULL, "Earpiece Mux"},
+       {"PREDRIVEL", NULL, "PredriveL Mux"},
+       {"PREDRIVER", NULL, "PredriveR Mux"},
+       {"HSOL", NULL, "HeadsetL Mux"},
+       {"HSOR", NULL, "HeadsetR Mux"},
+       {"CARKITL", NULL, "CarkitL Mux"},
+       {"CARKITR", NULL, "CarkitR Mux"},
+       {"HFL", NULL, "HandsfreeL Mux"},
+       {"HFR", NULL, "HandsfreeR Mux"},
+
+       /* inputs */
+       {"ADCL", NULL, "INL"},
+       {"ADCR", NULL, "INR"},
+};
+
+static int twl4030_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, twl4030_dapm_widgets,
+                                ARRAY_SIZE(twl4030_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+       snd_soc_dapm_new_widgets(codec);
+       return 0;
+}
+
+static void twl4030_power_up(struct snd_soc_codec *codec)
+{
+       u8 anamicl, regmisc1, byte, popn;
+       int i = 0;
+
+       /* set CODECPDZ to turn on codec */
+       twl4030_set_codecpdz(codec);
+
+       /* initiate offset cancellation */
+       anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
+       twl4030_write(codec, TWL4030_REG_ANAMICL,
+               anamicl | TWL4030_CNCL_OFFSET_START);
+
+       /* wait for offset cancellation to complete */
+       do {
+               /* this takes a little while, so don't slam i2c */
+               udelay(2000);
+               twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
+                                   TWL4030_REG_ANAMICL);
+       } while ((i++ < 100) &&
+                ((byte & TWL4030_CNCL_OFFSET_START) ==
+                 TWL4030_CNCL_OFFSET_START));
+
+       /* anti-pop when changing analog gain */
+       regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1);
+       twl4030_write(codec, TWL4030_REG_MISC_SET_1,
+               regmisc1 | TWL4030_SMOOTH_ANAVOL_EN);
+
+       /* toggle CODECPDZ as per TRM */
+       twl4030_clear_codecpdz(codec);
+       twl4030_set_codecpdz(codec);
+
+       /* program anti-pop with bias ramp delay */
+       popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
+       popn &= TWL4030_RAMP_DELAY;
+       popn |= TWL4030_RAMP_DELAY_645MS;
+       twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
+       popn |= TWL4030_VMID_EN;
+       twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
+
+       /* enable anti-pop ramp */
+       popn |= TWL4030_RAMP_EN;
+       twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
+}
+
+static void twl4030_power_down(struct snd_soc_codec *codec)
+{
+       u8 popn;
+
+       /* disable anti-pop ramp */
+       popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
+       popn &= ~TWL4030_RAMP_EN;
+       twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
+
+       /* disable bias out */
+       popn &= ~TWL4030_VMID_EN;
+       twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
+
+       /* power down */
+       twl4030_clear_codecpdz(codec);
+}
+
+static int twl4030_set_bias_level(struct snd_soc_codec *codec,
+                                 enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               twl4030_power_up(codec);
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               /* TODO: develop a twl4030_prepare function */
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               /* TODO: develop a twl4030_standby function */
+               twl4030_power_down(codec);
+               break;
+       case SND_SOC_BIAS_OFF:
+               twl4030_power_down(codec);
+               break;
+       }
+       codec->bias_level = level;
+
+       return 0;
+}
+
+static int twl4030_hw_params(struct snd_pcm_substream *substream,
+                          struct snd_pcm_hw_params *params,
+                          struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->codec;
+       u8 mode, old_mode, format, old_format;
+
+
+       /* bit rate */
+       old_mode = twl4030_read_reg_cache(codec,
+                       TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
+       mode = old_mode & ~TWL4030_APLL_RATE;
+
+       switch (params_rate(params)) {
+       case 8000:
+               mode |= TWL4030_APLL_RATE_8000;
+               break;
+       case 11025:
+               mode |= TWL4030_APLL_RATE_11025;
+               break;
+       case 12000:
+               mode |= TWL4030_APLL_RATE_12000;
+               break;
+       case 16000:
+               mode |= TWL4030_APLL_RATE_16000;
+               break;
+       case 22050:
+               mode |= TWL4030_APLL_RATE_22050;
+               break;
+       case 24000:
+               mode |= TWL4030_APLL_RATE_24000;
+               break;
+       case 32000:
+               mode |= TWL4030_APLL_RATE_32000;
+               break;
+       case 44100:
+               mode |= TWL4030_APLL_RATE_44100;
+               break;
+       case 48000:
+               mode |= TWL4030_APLL_RATE_48000;
+               break;
+       default:
+               printk(KERN_ERR "TWL4030 hw params: unknown rate %d\n",
+                       params_rate(params));
+               return -EINVAL;
+       }
+
+       if (mode != old_mode) {
+               /* change rate and set CODECPDZ */
+               twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+               twl4030_set_codecpdz(codec);
+       }
+
+       /* sample size */
+       old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
+       format = old_format;
+       format &= ~TWL4030_DATA_WIDTH;
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               format |= TWL4030_DATA_WIDTH_16S_16W;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               format |= TWL4030_DATA_WIDTH_32S_24W;
+               break;
+       default:
+               printk(KERN_ERR "TWL4030 hw params: unknown format %d\n",
+                       params_format(params));
+               return -EINVAL;
+       }
+
+       if (format != old_format) {
+
+               /* clear CODECPDZ before changing format (codec requirement) */
+               twl4030_clear_codecpdz(codec);
+
+               /* change format */
+               twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+
+               /* set CODECPDZ afterwards */
+               twl4030_set_codecpdz(codec);
+       }
+       return 0;
+}
+
+static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u8 infreq;
+
+       switch (freq) {
+       case 19200000:
+               infreq = TWL4030_APLL_INFREQ_19200KHZ;
+               break;
+       case 26000000:
+               infreq = TWL4030_APLL_INFREQ_26000KHZ;
+               break;
+       case 38400000:
+               infreq = TWL4030_APLL_INFREQ_38400KHZ;
+               break;
+       default:
+               printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n",
+                       freq);
+               return -EINVAL;
+       }
+
+       infreq |= TWL4030_APLL_EN;
+       twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
+
+       return 0;
+}
+
+static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
+                            unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u8 old_format, format;
+
+       /* get format */
+       old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
+       format = old_format;
+
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               format &= ~(TWL4030_AIF_SLAVE_EN);
+               format &= ~(TWL4030_CLK256FS_EN);
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               format |= TWL4030_AIF_SLAVE_EN;
+               format |= TWL4030_CLK256FS_EN;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* interface format */
+       format &= ~TWL4030_AIF_FORMAT;
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               format |= TWL4030_AIF_FORMAT_CODEC;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (format != old_format) {
+
+               /* clear CODECPDZ before changing format (codec requirement) */
+               twl4030_clear_codecpdz(codec);
+
+               /* change format */
+               twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+
+               /* set CODECPDZ afterwards */
+               twl4030_set_codecpdz(codec);
+       }
+
+       return 0;
+}
+
+#define TWL4030_RATES   (SNDRV_PCM_RATE_8000_48000)
+#define TWL4030_FORMATS         (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE)
+
+struct snd_soc_dai twl4030_dai = {
+       .name = "twl4030",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = TWL4030_RATES,
+               .formats = TWL4030_FORMATS,},
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = TWL4030_RATES,
+               .formats = TWL4030_FORMATS,},
+       .ops = {
+               .hw_params = twl4030_hw_params,
+               .set_sysclk = twl4030_set_dai_sysclk,
+               .set_fmt = twl4030_set_dai_fmt,
+       }
+};
+EXPORT_SYMBOL_GPL(twl4030_dai);
+
+static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->codec;
+
+       twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int twl4030_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->codec;
+
+       twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       twl4030_set_bias_level(codec, codec->suspend_bias_level);
+       return 0;
+}
+
+/*
+ * initialize the driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+
+static int twl4030_init(struct snd_soc_device *socdev)
+{
+       struct snd_soc_codec *codec = socdev->codec;
+       int ret = 0;
+
+       printk(KERN_INFO "TWL4030 Audio Codec init \n");
+
+       codec->name = "twl4030";
+       codec->owner = THIS_MODULE;
+       codec->read = twl4030_read_reg_cache;
+       codec->write = twl4030_write;
+       codec->set_bias_level = twl4030_set_bias_level;
+       codec->dai = &twl4030_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = sizeof(twl4030_reg);
+       codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
+                                       GFP_KERNEL);
+       if (codec->reg_cache == NULL)
+               return -ENOMEM;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               printk(KERN_ERR "twl4030: failed to create pcms\n");
+               goto pcm_err;
+       }
+
+       twl4030_init_chip(codec);
+
+       /* power on device */
+       twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       twl4030_add_controls(codec);
+       twl4030_add_widgets(codec);
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               printk(KERN_ERR "twl4030: failed to register card\n");
+               goto card_err;
+       }
+
+       return ret;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+pcm_err:
+       kfree(codec->reg_cache);
+       return ret;
+}
+
+static struct snd_soc_device *twl4030_socdev;
+
+static int twl4030_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+
+       codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+       if (codec == NULL)
+               return -ENOMEM;
+
+       socdev->codec = codec;
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       twl4030_socdev = socdev;
+       twl4030_init(socdev);
+
+       return 0;
+}
+
+static int twl4030_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->codec;
+
+       printk(KERN_INFO "TWL4030 Audio Codec remove\n");
+       kfree(codec);
+
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_twl4030 = {
+       .probe = twl4030_probe,
+       .remove = twl4030_remove,
+       .suspend = twl4030_suspend,
+       .resume = twl4030_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
+
+static int __init twl4030_modinit(void)
+{
+       return snd_soc_register_dai(&twl4030_dai);
+}
+module_init(twl4030_modinit);
+
+static void __exit twl4030_exit(void)
+{
+       snd_soc_unregister_dai(&twl4030_dai);
+}
+module_exit(twl4030_exit);
+
+MODULE_DESCRIPTION("ASoC TWL4030 codec driver");
+MODULE_AUTHOR("Steve Sakoman");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
new file mode 100644 (file)
index 0000000..54615c7
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * ALSA SoC TWL4030 codec driver
+ *
+ * Author: Steve Sakoman <steve@sakoman.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TWL4030_AUDIO_H__
+#define __TWL4030_AUDIO_H__
+
+#define TWL4030_REG_CODEC_MODE         0x1
+#define TWL4030_REG_OPTION             0x2
+#define TWL4030_REG_UNKNOWN            0x3
+#define TWL4030_REG_MICBIAS_CTL                0x4
+#define TWL4030_REG_ANAMICL            0x5
+#define TWL4030_REG_ANAMICR            0x6
+#define TWL4030_REG_AVADC_CTL          0x7
+#define TWL4030_REG_ADCMICSEL          0x8
+#define TWL4030_REG_DIGMIXING          0x9
+#define TWL4030_REG_ATXL1PGA           0xA
+#define TWL4030_REG_ATXR1PGA           0xB
+#define TWL4030_REG_AVTXL2PGA          0xC
+#define TWL4030_REG_AVTXR2PGA          0xD
+#define TWL4030_REG_AUDIO_IF           0xE
+#define TWL4030_REG_VOICE_IF           0xF
+#define TWL4030_REG_ARXR1PGA           0x10
+#define TWL4030_REG_ARXL1PGA           0x11
+#define TWL4030_REG_ARXR2PGA           0x12
+#define TWL4030_REG_ARXL2PGA           0x13
+#define TWL4030_REG_VRXPGA             0x14
+#define TWL4030_REG_VSTPGA             0x15
+#define TWL4030_REG_VRX2ARXPGA         0x16
+#define TWL4030_REG_AVDAC_CTL          0x17
+#define TWL4030_REG_ARX2VTXPGA         0x18
+#define TWL4030_REG_ARXL1_APGA_CTL     0x19
+#define TWL4030_REG_ARXR1_APGA_CTL     0x1A
+#define TWL4030_REG_ARXL2_APGA_CTL     0x1B
+#define TWL4030_REG_ARXR2_APGA_CTL     0x1C
+#define TWL4030_REG_ATX2ARXPGA         0x1D
+#define TWL4030_REG_BT_IF              0x1E
+#define TWL4030_REG_BTPGA              0x1F
+#define TWL4030_REG_BTSTPGA            0x20
+#define TWL4030_REG_EAR_CTL            0x21
+#define TWL4030_REG_HS_SEL             0x22
+#define TWL4030_REG_HS_GAIN_SET                0x23
+#define TWL4030_REG_HS_POPN_SET                0x24
+#define TWL4030_REG_PREDL_CTL          0x25
+#define TWL4030_REG_PREDR_CTL          0x26
+#define TWL4030_REG_PRECKL_CTL         0x27
+#define TWL4030_REG_PRECKR_CTL         0x28
+#define TWL4030_REG_HFL_CTL            0x29
+#define TWL4030_REG_HFR_CTL            0x2A
+#define TWL4030_REG_ALC_CTL            0x2B
+#define TWL4030_REG_ALC_SET1           0x2C
+#define TWL4030_REG_ALC_SET2           0x2D
+#define TWL4030_REG_BOOST_CTL          0x2E
+#define TWL4030_REG_SOFTVOL_CTL                0x2F
+#define TWL4030_REG_DTMF_FREQSEL       0x30
+#define TWL4030_REG_DTMF_TONEXT1H      0x31
+#define TWL4030_REG_DTMF_TONEXT1L      0x32
+#define TWL4030_REG_DTMF_TONEXT2H      0x33
+#define TWL4030_REG_DTMF_TONEXT2L      0x34
+#define TWL4030_REG_DTMF_TONOFF                0x35
+#define TWL4030_REG_DTMF_WANONOFF      0x36
+#define TWL4030_REG_I2S_RX_SCRAMBLE_H  0x37
+#define TWL4030_REG_I2S_RX_SCRAMBLE_M  0x38
+#define TWL4030_REG_I2S_RX_SCRAMBLE_L  0x39
+#define TWL4030_REG_APLL_CTL           0x3A
+#define TWL4030_REG_DTMF_CTL           0x3B
+#define TWL4030_REG_DTMF_PGA_CTL2      0x3C
+#define TWL4030_REG_DTMF_PGA_CTL1      0x3D
+#define TWL4030_REG_MISC_SET_1         0x3E
+#define TWL4030_REG_PCMBTMUX           0x3F
+#define TWL4030_REG_RX_PATH_SEL                0x43
+#define TWL4030_REG_VDL_APGA_CTL       0x44
+#define TWL4030_REG_VIBRA_CTL          0x45
+#define TWL4030_REG_VIBRA_SET          0x46
+#define TWL4030_REG_VIBRA_PWM_SET      0x47
+#define TWL4030_REG_ANAMIC_GAIN                0x48
+#define TWL4030_REG_MISC_SET_2         0x49
+
+#define TWL4030_CACHEREGNUM    (TWL4030_REG_MISC_SET_2 + 1)
+
+/* Bitfield Definitions */
+
+/* TWL4030_CODEC_MODE (0x01) Fields */
+
+#define TWL4030_APLL_RATE              0xF0
+#define TWL4030_APLL_RATE_8000         0x00
+#define TWL4030_APLL_RATE_11025                0x10
+#define TWL4030_APLL_RATE_12000                0x20
+#define TWL4030_APLL_RATE_16000                0x40
+#define TWL4030_APLL_RATE_22050                0x50
+#define TWL4030_APLL_RATE_24000                0x60
+#define TWL4030_APLL_RATE_32000                0x80
+#define TWL4030_APLL_RATE_44100                0x90
+#define TWL4030_APLL_RATE_48000                0xA0
+#define TWL4030_SEL_16K                        0x04
+#define TWL4030_CODECPDZ               0x02
+#define TWL4030_OPT_MODE               0x01
+
+/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
+
+#define TWL4030_MICBIAS2_CTL           0x40
+#define TWL4030_MICBIAS1_CTL           0x20
+#define TWL4030_HSMICBIAS_EN           0x04
+#define TWL4030_MICBIAS2_EN            0x02
+#define TWL4030_MICBIAS1_EN            0x01
+
+/* ANAMICL (0x05) Fields */
+
+#define TWL4030_CNCL_OFFSET_START      0x80
+#define TWL4030_OFFSET_CNCL_SEL                0x60
+#define TWL4030_OFFSET_CNCL_SEL_ARX1   0x00
+#define TWL4030_OFFSET_CNCL_SEL_ARX2   0x20
+#define TWL4030_OFFSET_CNCL_SEL_VRX    0x40
+#define TWL4030_OFFSET_CNCL_SEL_ALL    0x60
+#define TWL4030_MICAMPL_EN             0x10
+#define TWL4030_CKMIC_EN               0x08
+#define TWL4030_AUXL_EN                        0x04
+#define TWL4030_HSMIC_EN               0x02
+#define TWL4030_MAINMIC_EN             0x01
+
+/* ANAMICR (0x06) Fields */
+
+#define TWL4030_MICAMPR_EN             0x10
+#define TWL4030_AUXR_EN                        0x04
+#define TWL4030_SUBMIC_EN              0x01
+
+/* AVADC_CTL (0x07) Fields */
+
+#define TWL4030_ADCL_EN                        0x08
+#define TWL4030_AVADC_CLK_PRIORITY     0x04
+#define TWL4030_ADCR_EN                        0x02
+
+/* AUDIO_IF (0x0E) Fields */
+
+#define TWL4030_AIF_SLAVE_EN           0x80
+#define TWL4030_DATA_WIDTH             0x60
+#define TWL4030_DATA_WIDTH_16S_16W     0x00
+#define TWL4030_DATA_WIDTH_32S_16W     0x40
+#define TWL4030_DATA_WIDTH_32S_24W     0x60
+#define TWL4030_AIF_FORMAT             0x18
+#define TWL4030_AIF_FORMAT_CODEC       0x00
+#define TWL4030_AIF_FORMAT_LEFT                0x08
+#define TWL4030_AIF_FORMAT_RIGHT       0x10
+#define TWL4030_AIF_FORMAT_TDM         0x18
+#define TWL4030_AIF_TRI_EN             0x04
+#define TWL4030_CLK256FS_EN            0x02
+#define TWL4030_AIF_EN                 0x01
+
+/* HS_GAIN_SET (0x23) Fields */
+
+#define TWL4030_HSR_GAIN               0x0C
+#define TWL4030_HSR_GAIN_PWR_DOWN      0x00
+#define TWL4030_HSR_GAIN_PLUS_6DB      0x04
+#define TWL4030_HSR_GAIN_0DB           0x08
+#define TWL4030_HSR_GAIN_MINUS_6DB     0x0C
+#define TWL4030_HSL_GAIN               0x03
+#define TWL4030_HSL_GAIN_PWR_DOWN      0x00
+#define TWL4030_HSL_GAIN_PLUS_6DB      0x01
+#define TWL4030_HSL_GAIN_0DB           0x02
+#define TWL4030_HSL_GAIN_MINUS_6DB     0x03
+
+/* HS_POPN_SET (0x24) Fields */
+
+#define TWL4030_VMID_EN                        0x40
+#define        TWL4030_EXTMUTE                 0x20
+#define TWL4030_RAMP_DELAY             0x1C
+#define TWL4030_RAMP_DELAY_20MS                0x00
+#define TWL4030_RAMP_DELAY_40MS                0x04
+#define TWL4030_RAMP_DELAY_81MS                0x08
+#define TWL4030_RAMP_DELAY_161MS       0x0C
+#define TWL4030_RAMP_DELAY_323MS       0x10
+#define TWL4030_RAMP_DELAY_645MS       0x14
+#define TWL4030_RAMP_DELAY_1291MS      0x18
+#define TWL4030_RAMP_DELAY_2581MS      0x1C
+#define TWL4030_RAMP_EN                        0x02
+
+/* HFL_CTL (0x29, 0x2A) Fields */
+#define TWL4030_HF_CTL_HB_EN           0x04
+#define TWL4030_HF_CTL_LOOP_EN         0x08
+#define TWL4030_HF_CTL_RAMP_EN         0x10
+#define TWL4030_HF_CTL_REF_EN          0x20
+
+/* APLL_CTL (0x3A) Fields */
+
+#define TWL4030_APLL_EN                        0x10
+#define TWL4030_APLL_INFREQ            0x0F
+#define TWL4030_APLL_INFREQ_19200KHZ   0x05
+#define TWL4030_APLL_INFREQ_26000KHZ   0x06
+#define TWL4030_APLL_INFREQ_38400KHZ   0x0F
+
+/* REG_MISC_SET_1 (0x3E) Fields */
+
+#define TWL4030_CLK64_EN               0x80
+#define TWL4030_SCRAMBLE_EN            0x40
+#define TWL4030_FMLOOP_EN              0x20
+#define TWL4030_SMOOTH_ANAVOL_EN       0x02
+#define TWL4030_DIGMIC_LR_SWAP_EN      0x01
+
+extern struct snd_soc_dai twl4030_dai;
+extern struct snd_soc_codec_device soc_codec_dev_twl4030;
+
+#endif /* End of __TWL4030_AUDIO_H__ */
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
new file mode 100644 (file)
index 0000000..a2c5064
--- /dev/null
@@ -0,0 +1,668 @@
+/*
+ * uda134x.c  --  UDA134X ALSA SoC Codec driver
+ *
+ * Modifications by Christian Pellegrin <chripell@evolware.org>
+ *
+ * Copyright 2007 Dension Audio Systems Ltd.
+ * Author: Zoltan Devai
+ *
+ * Based on the WM87xx drivers by Liam Girdwood and Richard Purdie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include <sound/uda134x.h>
+#include <sound/l3.h>
+
+#include "uda134x.h"
+
+
+#define POWER_OFF_ON_STANDBY 1
+/*
+  ALSA SOC usually puts the device in standby mode when it's not used
+  for sometime. If you define POWER_OFF_ON_STANDBY the driver will
+  turn off the ADC/DAC when this callback is invoked and turn it back
+  on when needed. Unfortunately this will result in a very light bump
+  (it can be audible only with good earphones). If this bothers you
+  just comment this line, you will have slightly higher power
+  consumption . Please note that sending the L3 command for ADC is
+  enough to make the bump, so it doesn't make difference if you
+  completely take off power from the codec.
+ */
+
+#define UDA134X_RATES SNDRV_PCM_RATE_8000_48000
+#define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+               SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)
+
+struct uda134x_priv {
+       int sysclk;
+       int dai_fmt;
+
+       struct snd_pcm_substream *master_substream;
+       struct snd_pcm_substream *slave_substream;
+};
+
+/* In-data addresses are hard-coded into the reg-cache values */
+static const char uda134x_reg[UDA134X_REGS_NUM] = {
+       /* Extended address registers */
+       0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+       /* Status, data regs */
+       0x00, 0x83, 0x00, 0x40, 0x80, 0x00,
+};
+
+/*
+ * The codec has no support for reading its registers except for peak level...
+ */
+static inline unsigned int uda134x_read_reg_cache(struct snd_soc_codec *codec,
+       unsigned int reg)
+{
+       u8 *cache = codec->reg_cache;
+
+       if (reg >= UDA134X_REGS_NUM)
+               return -1;
+       return cache[reg];
+}
+
+/*
+ * Write the register cache
+ */
+static inline void uda134x_write_reg_cache(struct snd_soc_codec *codec,
+       u8 reg, unsigned int value)
+{
+       u8 *cache = codec->reg_cache;
+
+       if (reg >= UDA134X_REGS_NUM)
+               return;
+       cache[reg] = value;
+}
+
+/*
+ * Write to the uda134x registers
+ *
+ */
+static int uda134x_write(struct snd_soc_codec *codec, unsigned int reg,
+       unsigned int value)
+{
+       int ret;
+       u8 addr;
+       u8 data = value;
+       struct uda134x_platform_data *pd = codec->control_data;
+
+       pr_debug("%s reg: %02X, value:%02X\n", __func__, reg, value);
+
+       if (reg >= UDA134X_REGS_NUM) {
+               printk(KERN_ERR "%s unkown register: reg: %d",
+                      __func__, reg);
+               return -EINVAL;
+       }
+
+       uda134x_write_reg_cache(codec, reg, value);
+
+       switch (reg) {
+       case UDA134X_STATUS0:
+       case UDA134X_STATUS1:
+               addr = UDA134X_STATUS_ADDR;
+               break;
+       case UDA134X_DATA000:
+       case UDA134X_DATA001:
+       case UDA134X_DATA010:
+               addr = UDA134X_DATA0_ADDR;
+               break;
+       case UDA134X_DATA1:
+               addr = UDA134X_DATA1_ADDR;
+               break;
+       default:
+               /* It's an extended address register */
+               addr =  (reg | UDA134X_EXTADDR_PREFIX);
+
+               ret = l3_write(&pd->l3,
+                              UDA134X_DATA0_ADDR, &addr, 1);
+               if (ret != 1)
+                       return -EIO;
+
+               addr = UDA134X_DATA0_ADDR;
+               data = (value | UDA134X_EXTDATA_PREFIX);
+               break;
+       }
+
+       ret = l3_write(&pd->l3,
+                      addr, &data, 1);
+       if (ret != 1)
+               return -EIO;
+
+       return 0;
+}
+
+static inline void uda134x_reset(struct snd_soc_codec *codec)
+{
+       u8 reset_reg = uda134x_read_reg_cache(codec, UDA134X_STATUS0);
+       uda134x_write(codec, UDA134X_STATUS0, reset_reg | (1<<6));
+       msleep(1);
+       uda134x_write(codec, UDA134X_STATUS0, reset_reg & ~(1<<6));
+}
+
+static int uda134x_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u8 mute_reg = uda134x_read_reg_cache(codec, UDA134X_DATA010);
+
+       pr_debug("%s mute: %d\n", __func__, mute);
+
+       if (mute)
+               mute_reg |= (1<<2);
+       else
+               mute_reg &= ~(1<<2);
+
+       uda134x_write(codec, UDA134X_DATA010, mute_reg & ~(1<<2));
+
+       return 0;
+}
+
+static int uda134x_startup(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->codec;
+       struct uda134x_priv *uda134x = codec->private_data;
+       struct snd_pcm_runtime *master_runtime;
+
+       if (uda134x->master_substream) {
+               master_runtime = uda134x->master_substream->runtime;
+
+               pr_debug("%s constraining to %d bits at %d\n", __func__,
+                        master_runtime->sample_bits,
+                        master_runtime->rate);
+
+               snd_pcm_hw_constraint_minmax(substream->runtime,
+                                            SNDRV_PCM_HW_PARAM_RATE,
+                                            master_runtime->rate,
+                                            master_runtime->rate);
+
+               snd_pcm_hw_constraint_minmax(substream->runtime,
+                                            SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+                                            master_runtime->sample_bits,
+                                            master_runtime->sample_bits);
+
+               uda134x->slave_substream = substream;
+       } else
+               uda134x->master_substream = substream;
+
+       return 0;
+}
+
+static void uda134x_shutdown(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->codec;
+       struct uda134x_priv *uda134x = codec->private_data;
+
+       if (uda134x->master_substream == substream)
+               uda134x->master_substream = uda134x->slave_substream;
+
+       uda134x->slave_substream = NULL;
+}
+
+static int uda134x_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params,
+       struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->codec;
+       struct uda134x_priv *uda134x = codec->private_data;
+       u8 hw_params;
+
+       if (substream == uda134x->slave_substream) {
+               pr_debug("%s ignoring hw_params for slave substream\n",
+                        __func__);
+               return 0;
+       }
+
+       hw_params = uda134x_read_reg_cache(codec, UDA134X_STATUS0);
+       hw_params &= STATUS0_SYSCLK_MASK;
+       hw_params &= STATUS0_DAIFMT_MASK;
+
+       pr_debug("%s sysclk: %d, rate:%d\n", __func__,
+                uda134x->sysclk, params_rate(params));
+
+       /* set SYSCLK / fs ratio */
+       switch (uda134x->sysclk / params_rate(params)) {
+       case 512:
+               break;
+       case 384:
+               hw_params |= (1<<4);
+               break;
+       case 256:
+               hw_params |= (1<<5);
+               break;
+       default:
+               printk(KERN_ERR "%s unsupported fs\n", __func__);
+               return -EINVAL;
+       }
+
+       pr_debug("%s dai_fmt: %d, params_format:%d\n", __func__,
+                uda134x->dai_fmt, params_format(params));
+
+       /* set DAI format and word length */
+       switch (uda134x->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               switch (params_format(params)) {
+               case SNDRV_PCM_FORMAT_S16_LE:
+                       hw_params |= (1<<1);
+                       break;
+               case SNDRV_PCM_FORMAT_S18_3LE:
+                       hw_params |= (1<<2);
+                       break;
+               case SNDRV_PCM_FORMAT_S20_3LE:
+                       hw_params |= ((1<<2) | (1<<1));
+                       break;
+               default:
+                       printk(KERN_ERR "%s unsupported format (right)\n",
+                              __func__);
+                       return -EINVAL;
+               }
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               hw_params |= (1<<3);
+               break;
+       default:
+               printk(KERN_ERR "%s unsupported format\n", __func__);
+               return -EINVAL;
+       }
+
+       uda134x_write(codec, UDA134X_STATUS0, hw_params);
+
+       return 0;
+}
+
+static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+                                 int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct uda134x_priv *uda134x = codec->private_data;
+
+       pr_debug("%s clk_id: %d, freq: %d, dir: %d\n", __func__,
+                clk_id, freq, dir);
+
+       /* Anything between 256fs*8Khz and 512fs*48Khz should be acceptable
+          because the codec is slave. Of course limitations of the clock
+          master (the IIS controller) apply.
+          We'll error out on set_hw_params if it's not OK */
+       if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) {
+               uda134x->sysclk = freq;
+               return 0;
+       }
+
+       printk(KERN_ERR "%s unsupported sysclk\n", __func__);
+       return -EINVAL;
+}
+
+static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
+                              unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct uda134x_priv *uda134x = codec->private_data;
+
+       pr_debug("%s fmt: %08X\n", __func__, fmt);
+
+       /* codec supports only full slave mode */
+       if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
+               printk(KERN_ERR "%s unsupported slave mode\n", __func__);
+               return -EINVAL;
+       }
+
+       /* no support for clock inversion */
+       if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) {
+               printk(KERN_ERR "%s unsupported clock inversion\n", __func__);
+               return -EINVAL;
+       }
+
+       /* We can't setup DAI format here as it depends on the word bit num */
+       /* so let's just store the value for later */
+       uda134x->dai_fmt = fmt;
+
+       return 0;
+}
+
+static int uda134x_set_bias_level(struct snd_soc_codec *codec,
+                                 enum snd_soc_bias_level level)
+{
+       u8 reg;
+       struct uda134x_platform_data *pd = codec->control_data;
+       int i;
+       u8 *cache = codec->reg_cache;
+
+       pr_debug("%s bias level %d\n", __func__, level);
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               /* ADC, DAC on */
+               reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
+               uda134x_write(codec, UDA134X_STATUS1, reg | 0x03);
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               /* power on */
+               if (pd->power) {
+                       pd->power(1);
+                       /* Sync reg_cache with the hardware */
+                       for (i = 0; i < ARRAY_SIZE(uda134x_reg); i++)
+                               codec->write(codec, i, *cache++);
+               }
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               /* ADC, DAC power off */
+               reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
+               uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03));
+               break;
+       case SND_SOC_BIAS_OFF:
+               /* power off */
+               if (pd->power)
+                       pd->power(0);
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+static const char *uda134x_dsp_setting[] = {"Flat", "Minimum1",
+                                           "Minimum2", "Maximum"};
+static const char *uda134x_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+static const char *uda134x_mixmode[] = {"Differential", "Analog1",
+                                       "Analog2", "Both"};
+
+static const struct soc_enum uda134x_mixer_enum[] = {
+SOC_ENUM_SINGLE(UDA134X_DATA010, 0, 0x04, uda134x_dsp_setting),
+SOC_ENUM_SINGLE(UDA134X_DATA010, 3, 0x04, uda134x_deemph),
+SOC_ENUM_SINGLE(UDA134X_EA010, 0, 0x04, uda134x_mixmode),
+};
+
+static const struct snd_kcontrol_new uda1341_snd_controls[] = {
+SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
+SOC_SINGLE("Capture Volume", UDA134X_EA010, 2, 0x07, 0),
+SOC_SINGLE("Analog1 Volume", UDA134X_EA000, 0, 0x1F, 1),
+SOC_SINGLE("Analog2 Volume", UDA134X_EA001, 0, 0x1F, 1),
+
+SOC_SINGLE("Mic Sensitivity", UDA134X_EA010, 2, 7, 0),
+SOC_SINGLE("Mic Volume", UDA134X_EA101, 0, 0x1F, 0),
+
+SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
+SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
+
+SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
+SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
+SOC_ENUM("Input Mux", uda134x_mixer_enum[2]),
+
+SOC_SINGLE("AGC Switch", UDA134X_EA100, 4, 1, 0),
+SOC_SINGLE("AGC Target Volume", UDA134X_EA110, 0, 0x03, 1),
+SOC_SINGLE("AGC Timing", UDA134X_EA110, 2, 0x07, 0),
+
+SOC_SINGLE("DAC +6dB Switch", UDA134X_STATUS1, 6, 1, 0),
+SOC_SINGLE("ADC +6dB Switch", UDA134X_STATUS1, 5, 1, 0),
+SOC_SINGLE("ADC Polarity Switch", UDA134X_STATUS1, 4, 1, 0),
+SOC_SINGLE("DAC Polarity Switch", UDA134X_STATUS1, 3, 1, 0),
+SOC_SINGLE("Double Speed Playback Switch", UDA134X_STATUS1, 2, 1, 0),
+SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new uda1340_snd_controls[] = {
+SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
+
+SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
+SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
+
+SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
+SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
+
+SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
+};
+
+static int uda134x_add_controls(struct snd_soc_codec *codec)
+{
+       int err, i, n;
+       const struct snd_kcontrol_new *ctrls;
+       struct uda134x_platform_data *pd = codec->control_data;
+
+       switch (pd->model) {
+       case UDA134X_UDA1340:
+       case UDA134X_UDA1344:
+               n = ARRAY_SIZE(uda1340_snd_controls);
+               ctrls = uda1340_snd_controls;
+               break;
+       case UDA134X_UDA1341:
+               n = ARRAY_SIZE(uda1341_snd_controls);
+               ctrls = uda1341_snd_controls;
+               break;
+       default:
+               printk(KERN_ERR "%s unkown codec type: %d",
+                      __func__, pd->model);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < n; i++) {
+               err = snd_ctl_add(codec->card,
+                                 snd_soc_cnew(&ctrls[i],
+                                              codec, NULL));
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+struct snd_soc_dai uda134x_dai = {
+       .name = "UDA134X",
+       /* playback capabilities */
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = UDA134X_RATES,
+               .formats = UDA134X_FORMATS,
+       },
+       /* capture capabilities */
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = UDA134X_RATES,
+               .formats = UDA134X_FORMATS,
+       },
+       /* pcm operations */
+       .ops = {
+               .startup = uda134x_startup,
+               .shutdown = uda134x_shutdown,
+               .hw_params = uda134x_hw_params,
+               .digital_mute = uda134x_mute,
+               .set_sysclk = uda134x_set_dai_sysclk,
+               .set_fmt = uda134x_set_dai_fmt,
+       }
+};
+EXPORT_SYMBOL(uda134x_dai);
+
+
+static int uda134x_soc_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       struct uda134x_priv *uda134x;
+       void *codec_setup_data = socdev->codec_data;
+       int ret = -ENOMEM;
+       struct uda134x_platform_data *pd;
+
+       printk(KERN_INFO "UDA134X SoC Audio Codec\n");
+
+       if (!codec_setup_data) {
+               printk(KERN_ERR "UDA134X SoC codec: "
+                      "missing L3 bitbang function\n");
+               return -ENODEV;
+       }
+
+       pd = codec_setup_data;
+       switch (pd->model) {
+       case UDA134X_UDA1340:
+       case UDA134X_UDA1341:
+       case UDA134X_UDA1344:
+               break;
+       default:
+               printk(KERN_ERR "UDA134X SoC codec: "
+                      "unsupported model %d\n",
+                       pd->model);
+               return -EINVAL;
+       }
+
+       socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+       if (socdev->codec == NULL)
+               return ret;
+
+       codec = socdev->codec;
+
+       uda134x = kzalloc(sizeof(struct uda134x_priv), GFP_KERNEL);
+       if (uda134x == NULL)
+               goto priv_err;
+       codec->private_data = uda134x;
+
+       codec->reg_cache = kmemdup(uda134x_reg, sizeof(uda134x_reg),
+                                  GFP_KERNEL);
+       if (codec->reg_cache == NULL)
+               goto reg_err;
+
+       mutex_init(&codec->mutex);
+
+       codec->reg_cache_size = sizeof(uda134x_reg);
+       codec->reg_cache_step = 1;
+
+       codec->name = "UDA134X";
+       codec->owner = THIS_MODULE;
+       codec->dai = &uda134x_dai;
+       codec->num_dai = 1;
+       codec->read = uda134x_read_reg_cache;
+       codec->write = uda134x_write;
+#ifdef POWER_OFF_ON_STANDBY
+       codec->set_bias_level = uda134x_set_bias_level;
+#endif
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->control_data = codec_setup_data;
+
+       if (pd->power)
+               pd->power(1);
+
+       uda134x_reset(codec);
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               printk(KERN_ERR "UDA134X: failed to register pcms\n");
+               goto pcm_err;
+       }
+
+       ret = uda134x_add_controls(codec);
+       if (ret < 0) {
+               printk(KERN_ERR "UDA134X: failed to register controls\n");
+               goto pcm_err;
+       }
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               printk(KERN_ERR "UDA134X: failed to register card\n");
+               goto card_err;
+       }
+
+       return 0;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+pcm_err:
+       kfree(codec->reg_cache);
+reg_err:
+       kfree(codec->private_data);
+priv_err:
+       kfree(codec);
+       return ret;
+}
+
+/* power down chip */
+static int uda134x_soc_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->codec;
+
+       uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       kfree(codec->private_data);
+       kfree(codec->reg_cache);
+       kfree(codec);
+
+       return 0;
+}
+
+#if defined(CONFIG_PM)
+static int uda134x_soc_suspend(struct platform_device *pdev,
+                                               pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->codec;
+
+       uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int uda134x_soc_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->codec;
+
+       uda134x_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+       uda134x_set_bias_level(codec, SND_SOC_BIAS_ON);
+       return 0;
+}
+#else
+#define uda134x_soc_suspend NULL
+#define uda134x_soc_resume NULL
+#endif /* CONFIG_PM */
+
+struct snd_soc_codec_device soc_codec_dev_uda134x = {
+       .probe =        uda134x_soc_probe,
+       .remove =       uda134x_soc_remove,
+       .suspend =      uda134x_soc_suspend,
+       .resume =       uda134x_soc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_uda134x);
+
+static int __init uda134x_init(void)
+{
+       return snd_soc_register_dai(&uda134x_dai);
+}
+module_init(uda134x_init);
+
+static void __exit uda134x_exit(void)
+{
+       snd_soc_unregister_dai(&uda134x_dai);
+}
+module_exit(uda134x_exit);
+
+MODULE_DESCRIPTION("UDA134X ALSA soc codec driver");
+MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/uda134x.h b/sound/soc/codecs/uda134x.h
new file mode 100644 (file)
index 0000000..94f4404
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef _UDA134X_CODEC_H
+#define _UDA134X_CODEC_H
+
+#define UDA134X_L3ADDR 5
+#define UDA134X_DATA0_ADDR     ((UDA134X_L3ADDR << 2) | 0)
+#define UDA134X_DATA1_ADDR     ((UDA134X_L3ADDR << 2) | 1)
+#define UDA134X_STATUS_ADDR    ((UDA134X_L3ADDR << 2) | 2)
+
+#define UDA134X_EXTADDR_PREFIX 0xC0
+#define UDA134X_EXTDATA_PREFIX 0xE0
+
+/* UDA134X registers */
+#define UDA134X_EA000  0
+#define UDA134X_EA001  1
+#define UDA134X_EA010  2
+#define UDA134X_EA011  3
+#define UDA134X_EA100  4
+#define UDA134X_EA101  5
+#define UDA134X_EA110  6
+#define UDA134X_EA111  7
+#define UDA134X_STATUS0 8
+#define UDA134X_STATUS1 9
+#define UDA134X_DATA000 10
+#define UDA134X_DATA001 11
+#define UDA134X_DATA010 12
+#define UDA134X_DATA1  13
+
+#define UDA134X_REGS_NUM 14
+
+#define STATUS0_DAIFMT_MASK (~(7<<1))
+#define STATUS0_SYSCLK_MASK (~(3<<4))
+
+extern struct snd_soc_dai uda134x_dai;
+extern struct snd_soc_codec_device soc_codec_dev_uda134x;
+
+#endif
index a69ee72..e6bf084 100644 (file)
@@ -407,7 +407,8 @@ static int uda1380_set_dai_fmt(struct snd_soc_dai *codec_dai,
  * when the DAI is being clocked by the CPU DAI. It's up to the
  * machine and cpu DAI driver to do this before we are called.
  */
-static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
+static int uda1380_pcm_prepare(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -439,7 +440,8 @@ static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
 }
 
 static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -477,7 +479,8 @@ static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream)
+static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream,
+                                struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -560,8 +563,6 @@ struct snd_soc_dai uda1380_dai[] = {
                .hw_params = uda1380_pcm_hw_params,
                .shutdown = uda1380_pcm_shutdown,
                .prepare = uda1380_pcm_prepare,
-       },
-       .dai_ops = {
                .digital_mute = uda1380_mute,
                .set_fmt = uda1380_set_dai_fmt,
        },
@@ -579,8 +580,6 @@ struct snd_soc_dai uda1380_dai[] = {
                .hw_params = uda1380_pcm_hw_params,
                .shutdown = uda1380_pcm_shutdown,
                .prepare = uda1380_pcm_prepare,
-       },
-       .dai_ops = {
                .digital_mute = uda1380_mute,
                .set_fmt = uda1380_set_dai_fmt,
        },
@@ -598,8 +597,6 @@ struct snd_soc_dai uda1380_dai[] = {
                .hw_params = uda1380_pcm_hw_params,
                .shutdown = uda1380_pcm_shutdown,
                .prepare = uda1380_pcm_prepare,
-       },
-       .dai_ops = {
                .set_fmt = uda1380_set_dai_fmt,
        },
 },
@@ -680,7 +677,7 @@ static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
        /* uda1380 init */
        uda1380_add_controls(codec);
        uda1380_add_widgets(codec);
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                pr_err("uda1380: failed to register card\n");
                goto card_err;
@@ -844,6 +841,18 @@ struct snd_soc_codec_device soc_codec_dev_uda1380 = {
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
 
+static int __init uda1380_modinit(void)
+{
+       return snd_soc_register_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+}
+module_init(uda1380_modinit);
+
+static void __exit uda1380_exit(void)
+{
+       snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+}
+module_exit(uda1380_exit);
+
 MODULE_AUTHOR("Giorgio Padrin");
 MODULE_DESCRIPTION("Audio support for codec Philips UDA1380");
 MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
new file mode 100644 (file)
index 0000000..e3989d4
--- /dev/null
@@ -0,0 +1,1583 @@
+/*
+ * wm8350.c -- WM8350 ALSA SoC audio driver
+ *
+ * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood <lg@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wm8350/audio.h>
+#include <linux/mfd/wm8350/core.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8350.h"
+
+#define WM8350_OUTn_0dB 0x39
+
+#define WM8350_RAMP_NONE       0
+#define WM8350_RAMP_UP         1
+#define WM8350_RAMP_DOWN       2
+
+/* We only include the analogue supplies here; the digital supplies
+ * need to be available well before this driver can be probed.
+ */
+static const char *supply_names[] = {
+       "AVDD",
+       "HPVDD",
+};
+
+struct wm8350_output {
+       u16 active;
+       u16 left_vol;
+       u16 right_vol;
+       u16 ramp;
+       u16 mute;
+};
+
+struct wm8350_data {
+       struct snd_soc_codec codec;
+       struct wm8350_output out1;
+       struct wm8350_output out2;
+       struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+};
+
+static unsigned int wm8350_codec_cache_read(struct snd_soc_codec *codec,
+                                           unsigned int reg)
+{
+       struct wm8350 *wm8350 = codec->control_data;
+       return wm8350->reg_cache[reg];
+}
+
+static unsigned int wm8350_codec_read(struct snd_soc_codec *codec,
+                                     unsigned int reg)
+{
+       struct wm8350 *wm8350 = codec->control_data;
+       return wm8350_reg_read(wm8350, reg);
+}
+
+static int wm8350_codec_write(struct snd_soc_codec *codec, unsigned int reg,
+                             unsigned int value)
+{
+       struct wm8350 *wm8350 = codec->control_data;
+       return wm8350_reg_write(wm8350, reg, value);
+}
+
+/*
+ * Ramp OUT1 PGA volume to minimise pops at stream startup and shutdown.
+ */
+static inline int wm8350_out1_ramp_step(struct snd_soc_codec *codec)
+{
+       struct wm8350_data *wm8350_data = codec->private_data;
+       struct wm8350_output *out1 = &wm8350_data->out1;
+       struct wm8350 *wm8350 = codec->control_data;
+       int left_complete = 0, right_complete = 0;
+       u16 reg, val;
+
+       /* left channel */
+       reg = wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME);
+       val = (reg & WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+
+       if (out1->ramp == WM8350_RAMP_UP) {
+               /* ramp step up */
+               if (val < out1->left_vol) {
+                       val++;
+                       reg &= ~WM8350_OUT1L_VOL_MASK;
+                       wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME,
+                                        reg | (val << WM8350_OUT1L_VOL_SHIFT));
+               } else
+                       left_complete = 1;
+       } else if (out1->ramp == WM8350_RAMP_DOWN) {
+               /* ramp step down */
+               if (val > 0) {
+                       val--;
+                       reg &= ~WM8350_OUT1L_VOL_MASK;
+                       wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME,
+                                        reg | (val << WM8350_OUT1L_VOL_SHIFT));
+               } else
+                       left_complete = 1;
+       } else
+               return 1;
+
+       /* right channel */
+       reg = wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME);
+       val = (reg & WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+       if (out1->ramp == WM8350_RAMP_UP) {
+               /* ramp step up */
+               if (val < out1->right_vol) {
+                       val++;
+                       reg &= ~WM8350_OUT1R_VOL_MASK;
+                       wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME,
+                                        reg | (val << WM8350_OUT1R_VOL_SHIFT));
+               } else
+                       right_complete = 1;
+       } else if (out1->ramp == WM8350_RAMP_DOWN) {
+               /* ramp step down */
+               if (val > 0) {
+                       val--;
+                       reg &= ~WM8350_OUT1R_VOL_MASK;
+                       wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME,
+                                        reg | (val << WM8350_OUT1R_VOL_SHIFT));
+               } else
+                       right_complete = 1;
+       }
+
+       /* only hit the update bit if either volume has changed this step */
+       if (!left_complete || !right_complete)
+               wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME, WM8350_OUT1_VU);
+
+       return left_complete & right_complete;
+}
+
+/*
+ * Ramp OUT2 PGA volume to minimise pops at stream startup and shutdown.
+ */
+static inline int wm8350_out2_ramp_step(struct snd_soc_codec *codec)
+{
+       struct wm8350_data *wm8350_data = codec->private_data;
+       struct wm8350_output *out2 = &wm8350_data->out2;
+       struct wm8350 *wm8350 = codec->control_data;
+       int left_complete = 0, right_complete = 0;
+       u16 reg, val;
+
+       /* left channel */
+       reg = wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME);
+       val = (reg & WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+       if (out2->ramp == WM8350_RAMP_UP) {
+               /* ramp step up */
+               if (val < out2->left_vol) {
+                       val++;
+                       reg &= ~WM8350_OUT2L_VOL_MASK;
+                       wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME,
+                                        reg | (val << WM8350_OUT1L_VOL_SHIFT));
+               } else
+                       left_complete = 1;
+       } else if (out2->ramp == WM8350_RAMP_DOWN) {
+               /* ramp step down */
+               if (val > 0) {
+                       val--;
+                       reg &= ~WM8350_OUT2L_VOL_MASK;
+                       wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME,
+                                        reg | (val << WM8350_OUT1L_VOL_SHIFT));
+               } else
+                       left_complete = 1;
+       } else
+               return 1;
+
+       /* right channel */
+       reg = wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME);
+       val = (reg & WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+       if (out2->ramp == WM8350_RAMP_UP) {
+               /* ramp step up */
+               if (val < out2->right_vol) {
+                       val++;
+                       reg &= ~WM8350_OUT2R_VOL_MASK;
+                       wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME,
+                                        reg | (val << WM8350_OUT1R_VOL_SHIFT));
+               } else
+                       right_complete = 1;
+       } else if (out2->ramp == WM8350_RAMP_DOWN) {
+               /* ramp step down */
+               if (val > 0) {
+                       val--;
+                       reg &= ~WM8350_OUT2R_VOL_MASK;
+                       wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME,
+                                        reg | (val << WM8350_OUT1R_VOL_SHIFT));
+               } else
+                       right_complete = 1;
+       }
+
+       /* only hit the update bit if either volume has changed this step */
+       if (!left_complete || !right_complete)
+               wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME, WM8350_OUT2_VU);
+
+       return left_complete & right_complete;
+}
+
+/*
+ * This work ramps both output PGAs at stream start/stop time to
+ * minimise pop associated with DAPM power switching.
+ * It's best to enable Zero Cross when ramping occurs to minimise any
+ * zipper noises.
+ */
+static void wm8350_pga_work(struct work_struct *work)
+{
+       struct snd_soc_codec *codec =
+           container_of(work, struct snd_soc_codec, delayed_work.work);
+       struct wm8350_data *wm8350_data = codec->private_data;
+       struct wm8350_output *out1 = &wm8350_data->out1,
+           *out2 = &wm8350_data->out2;
+       int i, out1_complete, out2_complete;
+
+       /* do we need to ramp at all ? */
+       if (out1->ramp == WM8350_RAMP_NONE && out2->ramp == WM8350_RAMP_NONE)
+               return;
+
+       /* PGA volumes have 6 bits of resolution to ramp */
+       for (i = 0; i <= 63; i++) {
+               out1_complete = 1, out2_complete = 1;
+               if (out1->ramp != WM8350_RAMP_NONE)
+                       out1_complete = wm8350_out1_ramp_step(codec);
+               if (out2->ramp != WM8350_RAMP_NONE)
+                       out2_complete = wm8350_out2_ramp_step(codec);
+
+               /* ramp finished ? */
+               if (out1_complete && out2_complete)
+                       break;
+
+               /* we need to delay longer on the up ramp */
+               if (out1->ramp == WM8350_RAMP_UP ||
+                   out2->ramp == WM8350_RAMP_UP) {
+                       /* delay is longer over 0dB as increases are larger */
+                       if (i >= WM8350_OUTn_0dB)
+                               schedule_timeout_interruptible(msecs_to_jiffies
+                                                              (2));
+                       else
+                               schedule_timeout_interruptible(msecs_to_jiffies
+                                                              (1));
+               } else
+                       udelay(50);     /* doesn't matter if we delay longer */
+       }
+
+       out1->ramp = WM8350_RAMP_NONE;
+       out2->ramp = WM8350_RAMP_NONE;
+}
+
+/*
+ * WM8350 Controls
+ */
+
+static int pga_event(struct snd_soc_dapm_widget *w,
+                    struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct wm8350_data *wm8350_data = codec->private_data;
+       struct wm8350_output *out;
+
+       switch (w->shift) {
+       case 0:
+       case 1:
+               out = &wm8350_data->out1;
+               break;
+       case 2:
+       case 3:
+               out = &wm8350_data->out2;
+               break;
+
+       default:
+               BUG();
+               return -1;
+       }
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               out->ramp = WM8350_RAMP_UP;
+               out->active = 1;
+
+               if (!delayed_work_pending(&codec->delayed_work))
+                       schedule_delayed_work(&codec->delayed_work,
+                                             msecs_to_jiffies(1));
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               out->ramp = WM8350_RAMP_DOWN;
+               out->active = 0;
+
+               if (!delayed_work_pending(&codec->delayed_work))
+                       schedule_delayed_work(&codec->delayed_work,
+                                             msecs_to_jiffies(1));
+               break;
+       }
+
+       return 0;
+}
+
+static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct wm8350_data *wm8350_priv = codec->private_data;
+       struct wm8350_output *out = NULL;
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       int ret;
+       unsigned int reg = mc->reg;
+       u16 val;
+
+       /* For OUT1 and OUT2 we shadow the values and only actually write
+        * them out when active in order to ensure the amplifier comes on
+        * as quietly as possible. */
+       switch (reg) {
+       case WM8350_LOUT1_VOLUME:
+               out = &wm8350_priv->out1;
+               break;
+       case WM8350_LOUT2_VOLUME:
+               out = &wm8350_priv->out2;
+               break;
+       default:
+               break;
+       }
+
+       if (out) {
+               out->left_vol = ucontrol->value.integer.value[0];
+               out->right_vol = ucontrol->value.integer.value[1];
+               if (!out->active)
+                       return 1;
+       }
+
+       ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
+       if (ret < 0)
+               return ret;
+
+       /* now hit the volume update bits (always bit 8) */
+       val = wm8350_codec_read(codec, reg);
+       wm8350_codec_write(codec, reg, val | WM8350_OUT1_VU);
+       return 1;
+}
+
+static int wm8350_get_volsw_2r(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct wm8350_data *wm8350_priv = codec->private_data;
+       struct wm8350_output *out1 = &wm8350_priv->out1;
+       struct wm8350_output *out2 = &wm8350_priv->out2;
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+
+       /* If these are cached registers use the cache */
+       switch (reg) {
+       case WM8350_LOUT1_VOLUME:
+               ucontrol->value.integer.value[0] = out1->left_vol;
+               ucontrol->value.integer.value[1] = out1->right_vol;
+               return 0;
+
+       case WM8350_LOUT2_VOLUME:
+               ucontrol->value.integer.value[0] = out2->left_vol;
+               ucontrol->value.integer.value[1] = out2->right_vol;
+               return 0;
+
+       default:
+               break;
+       }
+
+       return snd_soc_get_volsw_2r(kcontrol, ucontrol);
+}
+
+/* double control with volume update */
+#define SOC_WM8350_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, \
+                               xinvert, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+               SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+               SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw_2r, \
+       .get = wm8350_get_volsw_2r, .put = wm8350_put_volsw_2r_vu, \
+       .private_value = (unsigned long)&(struct soc_mixer_control) \
+               {.reg = reg_left, .rreg = reg_right, .shift = xshift, \
+                .rshift = xshift, .max = xmax, .invert = xinvert}, }
+
+static const char *wm8350_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
+static const char *wm8350_pol[] = { "Normal", "Inv R", "Inv L", "Inv L & R" };
+static const char *wm8350_dacmutem[] = { "Normal", "Soft" };
+static const char *wm8350_dacmutes[] = { "Fast", "Slow" };
+static const char *wm8350_dacfilter[] = { "Normal", "Sloping" };
+static const char *wm8350_adcfilter[] = { "None", "High Pass" };
+static const char *wm8350_adchp[] = { "44.1kHz", "8kHz", "16kHz", "32kHz" };
+static const char *wm8350_lr[] = { "Left", "Right" };
+
+static const struct soc_enum wm8350_enum[] = {
+       SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 4, 4, wm8350_deemp),
+       SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 0, 4, wm8350_pol),
+       SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 14, 2, wm8350_dacmutem),
+       SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 13, 2, wm8350_dacmutes),
+       SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 12, 2, wm8350_dacfilter),
+       SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 15, 2, wm8350_adcfilter),
+       SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 8, 4, wm8350_adchp),
+       SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 0, 4, wm8350_pol),
+       SOC_ENUM_SINGLE(WM8350_INPUT_MIXER_VOLUME, 15, 2, wm8350_lr),
+};
+
+static DECLARE_TLV_DB_LINEAR(pre_amp_tlv, -1200, 3525);
+static DECLARE_TLV_DB_LINEAR(out_pga_tlv, -5700, 600);
+static DECLARE_TLV_DB_SCALE(dac_pcm_tlv, -7163, 36, 1);
+static DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -12700, 50, 1);
+static DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 1);
+
+static const unsigned int capture_sd_tlv[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 12, TLV_DB_SCALE_ITEM(-3600, 300, 1),
+       13, 15, TLV_DB_SCALE_ITEM(0, 0, 0),
+};
+
+static const struct snd_kcontrol_new wm8350_snd_controls[] = {
+       SOC_ENUM("Playback Deemphasis", wm8350_enum[0]),
+       SOC_ENUM("Playback DAC Inversion", wm8350_enum[1]),
+       SOC_WM8350_DOUBLE_R_TLV("Playback PCM Volume",
+                               WM8350_DAC_DIGITAL_VOLUME_L,
+                               WM8350_DAC_DIGITAL_VOLUME_R,
+                               0, 255, 0, dac_pcm_tlv),
+       SOC_ENUM("Playback PCM Mute Function", wm8350_enum[2]),
+       SOC_ENUM("Playback PCM Mute Speed", wm8350_enum[3]),
+       SOC_ENUM("Playback PCM Filter", wm8350_enum[4]),
+       SOC_ENUM("Capture PCM Filter", wm8350_enum[5]),
+       SOC_ENUM("Capture PCM HP Filter", wm8350_enum[6]),
+       SOC_ENUM("Capture ADC Inversion", wm8350_enum[7]),
+       SOC_WM8350_DOUBLE_R_TLV("Capture PCM Volume",
+                               WM8350_ADC_DIGITAL_VOLUME_L,
+                               WM8350_ADC_DIGITAL_VOLUME_R,
+                               0, 255, 0, adc_pcm_tlv),
+       SOC_DOUBLE_TLV("Capture Sidetone Volume",
+                      WM8350_ADC_DIVIDER,
+                      8, 4, 15, 1, capture_sd_tlv),
+       SOC_WM8350_DOUBLE_R_TLV("Capture Volume",
+                               WM8350_LEFT_INPUT_VOLUME,
+                               WM8350_RIGHT_INPUT_VOLUME,
+                               2, 63, 0, pre_amp_tlv),
+       SOC_DOUBLE_R("Capture ZC Switch",
+                    WM8350_LEFT_INPUT_VOLUME,
+                    WM8350_RIGHT_INPUT_VOLUME, 13, 1, 0),
+       SOC_SINGLE_TLV("Left Input Left Sidetone Volume",
+                      WM8350_OUTPUT_LEFT_MIXER_VOLUME, 1, 7, 0, out_mix_tlv),
+       SOC_SINGLE_TLV("Left Input Right Sidetone Volume",
+                      WM8350_OUTPUT_LEFT_MIXER_VOLUME,
+                      5, 7, 0, out_mix_tlv),
+       SOC_SINGLE_TLV("Left Input Bypass Volume",
+                      WM8350_OUTPUT_LEFT_MIXER_VOLUME,
+                      9, 7, 0, out_mix_tlv),
+       SOC_SINGLE_TLV("Right Input Left Sidetone Volume",
+                      WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
+                      1, 7, 0, out_mix_tlv),
+       SOC_SINGLE_TLV("Right Input Right Sidetone Volume",
+                      WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
+                      5, 7, 0, out_mix_tlv),
+       SOC_SINGLE_TLV("Right Input Bypass Volume",
+                      WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
+                      13, 7, 0, out_mix_tlv),
+       SOC_SINGLE("Left Input Mixer +20dB Switch",
+                  WM8350_INPUT_MIXER_VOLUME_L, 0, 1, 0),
+       SOC_SINGLE("Right Input Mixer +20dB Switch",
+                  WM8350_INPUT_MIXER_VOLUME_R, 0, 1, 0),
+       SOC_SINGLE_TLV("Out4 Capture Volume",
+                      WM8350_INPUT_MIXER_VOLUME,
+                      1, 7, 0, out_mix_tlv),
+       SOC_WM8350_DOUBLE_R_TLV("Out1 Playback Volume",
+                               WM8350_LOUT1_VOLUME,
+                               WM8350_ROUT1_VOLUME,
+                               2, 63, 0, out_pga_tlv),
+       SOC_DOUBLE_R("Out1 Playback ZC Switch",
+                    WM8350_LOUT1_VOLUME,
+                    WM8350_ROUT1_VOLUME, 13, 1, 0),
+       SOC_WM8350_DOUBLE_R_TLV("Out2 Playback Volume",
+                               WM8350_LOUT2_VOLUME,
+                               WM8350_ROUT2_VOLUME,
+                               2, 63, 0, out_pga_tlv),
+       SOC_DOUBLE_R("Out2 Playback ZC Switch", WM8350_LOUT2_VOLUME,
+                    WM8350_ROUT2_VOLUME, 13, 1, 0),
+       SOC_SINGLE("Out2 Right Invert Switch", WM8350_ROUT2_VOLUME, 10, 1, 0),
+       SOC_SINGLE_TLV("Out2 Beep Volume", WM8350_BEEP_VOLUME,
+                      5, 7, 0, out_mix_tlv),
+
+       SOC_DOUBLE_R("Out1 Playback Switch",
+                    WM8350_LOUT1_VOLUME,
+                    WM8350_ROUT1_VOLUME,
+                    14, 1, 1),
+       SOC_DOUBLE_R("Out2 Playback Switch",
+                    WM8350_LOUT2_VOLUME,
+                    WM8350_ROUT2_VOLUME,
+                    14, 1, 1),
+};
+
+/*
+ * DAPM Controls
+ */
+
+/* Left Playback Mixer */
+static const struct snd_kcontrol_new wm8350_left_play_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Playback Switch",
+                       WM8350_LEFT_MIXER_CONTROL, 11, 1, 0),
+       SOC_DAPM_SINGLE("Left Bypass Switch",
+                       WM8350_LEFT_MIXER_CONTROL, 2, 1, 0),
+       SOC_DAPM_SINGLE("Right Playback Switch",
+                       WM8350_LEFT_MIXER_CONTROL, 12, 1, 0),
+       SOC_DAPM_SINGLE("Left Sidetone Switch",
+                       WM8350_LEFT_MIXER_CONTROL, 0, 1, 0),
+       SOC_DAPM_SINGLE("Right Sidetone Switch",
+                       WM8350_LEFT_MIXER_CONTROL, 1, 1, 0),
+};
+
+/* Right Playback Mixer */
+static const struct snd_kcontrol_new wm8350_right_play_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Playback Switch",
+                       WM8350_RIGHT_MIXER_CONTROL, 12, 1, 0),
+       SOC_DAPM_SINGLE("Right Bypass Switch",
+                       WM8350_RIGHT_MIXER_CONTROL, 3, 1, 0),
+       SOC_DAPM_SINGLE("Left Playback Switch",
+                       WM8350_RIGHT_MIXER_CONTROL, 11, 1, 0),
+       SOC_DAPM_SINGLE("Left Sidetone Switch",
+                       WM8350_RIGHT_MIXER_CONTROL, 0, 1, 0),
+       SOC_DAPM_SINGLE("Right Sidetone Switch",
+                       WM8350_RIGHT_MIXER_CONTROL, 1, 1, 0),
+};
+
+/* Out4 Mixer */
+static const struct snd_kcontrol_new wm8350_out4_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Right Playback Switch",
+                       WM8350_OUT4_MIXER_CONTROL, 12, 1, 0),
+       SOC_DAPM_SINGLE("Left Playback Switch",
+                       WM8350_OUT4_MIXER_CONTROL, 11, 1, 0),
+       SOC_DAPM_SINGLE("Right Capture Switch",
+                       WM8350_OUT4_MIXER_CONTROL, 9, 1, 0),
+       SOC_DAPM_SINGLE("Out3 Playback Switch",
+                       WM8350_OUT4_MIXER_CONTROL, 2, 1, 0),
+       SOC_DAPM_SINGLE("Right Mixer Switch",
+                       WM8350_OUT4_MIXER_CONTROL, 1, 1, 0),
+       SOC_DAPM_SINGLE("Left Mixer Switch",
+                       WM8350_OUT4_MIXER_CONTROL, 0, 1, 0),
+};
+
+/* Out3 Mixer */
+static const struct snd_kcontrol_new wm8350_out3_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left Playback Switch",
+                       WM8350_OUT3_MIXER_CONTROL, 11, 1, 0),
+       SOC_DAPM_SINGLE("Left Capture Switch",
+                       WM8350_OUT3_MIXER_CONTROL, 8, 1, 0),
+       SOC_DAPM_SINGLE("Out4 Playback Switch",
+                       WM8350_OUT3_MIXER_CONTROL, 3, 1, 0),
+       SOC_DAPM_SINGLE("Left Mixer Switch",
+                       WM8350_OUT3_MIXER_CONTROL, 0, 1, 0),
+};
+
+/* Left Input Mixer */
+static const struct snd_kcontrol_new wm8350_left_capt_mixer_controls[] = {
+       SOC_DAPM_SINGLE_TLV("L2 Capture Volume",
+                           WM8350_INPUT_MIXER_VOLUME_L, 1, 7, 0, out_mix_tlv),
+       SOC_DAPM_SINGLE_TLV("L3 Capture Volume",
+                           WM8350_INPUT_MIXER_VOLUME_L, 9, 7, 0, out_mix_tlv),
+       SOC_DAPM_SINGLE("PGA Capture Switch",
+                       WM8350_LEFT_INPUT_VOLUME, 14, 1, 0),
+};
+
+/* Right Input Mixer */
+static const struct snd_kcontrol_new wm8350_right_capt_mixer_controls[] = {
+       SOC_DAPM_SINGLE_TLV("L2 Capture Volume",
+                           WM8350_INPUT_MIXER_VOLUME_R, 5, 7, 0, out_mix_tlv),
+       SOC_DAPM_SINGLE_TLV("L3 Capture Volume",
+                           WM8350_INPUT_MIXER_VOLUME_R, 13, 7, 0, out_mix_tlv),
+       SOC_DAPM_SINGLE("PGA Capture Switch",
+                       WM8350_RIGHT_INPUT_VOLUME, 14, 1, 0),
+};
+
+/* Left Mic Mixer */
+static const struct snd_kcontrol_new wm8350_left_mic_mixer_controls[] = {
+       SOC_DAPM_SINGLE("INN Capture Switch", WM8350_INPUT_CONTROL, 1, 1, 0),
+       SOC_DAPM_SINGLE("INP Capture Switch", WM8350_INPUT_CONTROL, 0, 1, 0),
+       SOC_DAPM_SINGLE("IN2 Capture Switch", WM8350_INPUT_CONTROL, 2, 1, 0),
+};
+
+/* Right Mic Mixer */
+static const struct snd_kcontrol_new wm8350_right_mic_mixer_controls[] = {
+       SOC_DAPM_SINGLE("INN Capture Switch", WM8350_INPUT_CONTROL, 9, 1, 0),
+       SOC_DAPM_SINGLE("INP Capture Switch", WM8350_INPUT_CONTROL, 8, 1, 0),
+       SOC_DAPM_SINGLE("IN2 Capture Switch", WM8350_INPUT_CONTROL, 10, 1, 0),
+};
+
+/* Beep Switch */
+static const struct snd_kcontrol_new wm8350_beep_switch_controls =
+SOC_DAPM_SINGLE("Switch", WM8350_BEEP_VOLUME, 15, 1, 1);
+
+/* Out4 Capture Mux */
+static const struct snd_kcontrol_new wm8350_out4_capture_controls =
+SOC_DAPM_ENUM("Route", wm8350_enum[8]);
+
+static const struct snd_soc_dapm_widget wm8350_dapm_widgets[] = {
+
+       SND_SOC_DAPM_PGA("IN3R PGA", WM8350_POWER_MGMT_2, 11, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IN3L PGA", WM8350_POWER_MGMT_2, 10, 0, NULL, 0),
+       SND_SOC_DAPM_PGA_E("Right Out2 PGA", WM8350_POWER_MGMT_3, 3, 0, NULL,
+                          0, pga_event,
+                          SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_PGA_E("Left Out2 PGA", WM8350_POWER_MGMT_3, 2, 0, NULL, 0,
+                          pga_event,
+                          SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_PGA_E("Right Out1 PGA", WM8350_POWER_MGMT_3, 1, 0, NULL,
+                          0, pga_event,
+                          SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_PGA_E("Left Out1 PGA", WM8350_POWER_MGMT_3, 0, 0, NULL, 0,
+                          pga_event,
+                          SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+       SND_SOC_DAPM_MIXER("Right Capture Mixer", WM8350_POWER_MGMT_2,
+                          7, 0, &wm8350_right_capt_mixer_controls[0],
+                          ARRAY_SIZE(wm8350_right_capt_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left Capture Mixer", WM8350_POWER_MGMT_2,
+                          6, 0, &wm8350_left_capt_mixer_controls[0],
+                          ARRAY_SIZE(wm8350_left_capt_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Out4 Mixer", WM8350_POWER_MGMT_2, 5, 0,
+                          &wm8350_out4_mixer_controls[0],
+                          ARRAY_SIZE(wm8350_out4_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Out3 Mixer", WM8350_POWER_MGMT_2, 4, 0,
+                          &wm8350_out3_mixer_controls[0],
+                          ARRAY_SIZE(wm8350_out3_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right Playback Mixer", WM8350_POWER_MGMT_2, 1, 0,
+                          &wm8350_right_play_mixer_controls[0],
+                          ARRAY_SIZE(wm8350_right_play_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left Playback Mixer", WM8350_POWER_MGMT_2, 0, 0,
+                          &wm8350_left_play_mixer_controls[0],
+                          ARRAY_SIZE(wm8350_left_play_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left Mic Mixer", WM8350_POWER_MGMT_2, 8, 0,
+                          &wm8350_left_mic_mixer_controls[0],
+                          ARRAY_SIZE(wm8350_left_mic_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right Mic Mixer", WM8350_POWER_MGMT_2, 9, 0,
+                          &wm8350_right_mic_mixer_controls[0],
+                          ARRAY_SIZE(wm8350_right_mic_mixer_controls)),
+
+       /* virtual mixer for Beep and Out2R */
+       SND_SOC_DAPM_MIXER("Out2 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_SWITCH("Beep", WM8350_POWER_MGMT_3, 7, 0,
+                           &wm8350_beep_switch_controls),
+
+       SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
+                        WM8350_POWER_MGMT_4, 3, 0),
+       SND_SOC_DAPM_ADC("Left ADC", "Left Capture",
+                        WM8350_POWER_MGMT_4, 2, 0),
+       SND_SOC_DAPM_DAC("Right DAC", "Right Playback",
+                        WM8350_POWER_MGMT_4, 5, 0),
+       SND_SOC_DAPM_DAC("Left DAC", "Left Playback",
+                        WM8350_POWER_MGMT_4, 4, 0),
+
+       SND_SOC_DAPM_MICBIAS("Mic Bias", WM8350_POWER_MGMT_1, 4, 0),
+
+       SND_SOC_DAPM_MUX("Out4 Capture Channel", SND_SOC_NOPM, 0, 0,
+                        &wm8350_out4_capture_controls),
+
+       SND_SOC_DAPM_OUTPUT("OUT1R"),
+       SND_SOC_DAPM_OUTPUT("OUT1L"),
+       SND_SOC_DAPM_OUTPUT("OUT2R"),
+       SND_SOC_DAPM_OUTPUT("OUT2L"),
+       SND_SOC_DAPM_OUTPUT("OUT3"),
+       SND_SOC_DAPM_OUTPUT("OUT4"),
+
+       SND_SOC_DAPM_INPUT("IN1RN"),
+       SND_SOC_DAPM_INPUT("IN1RP"),
+       SND_SOC_DAPM_INPUT("IN2R"),
+       SND_SOC_DAPM_INPUT("IN1LP"),
+       SND_SOC_DAPM_INPUT("IN1LN"),
+       SND_SOC_DAPM_INPUT("IN2L"),
+       SND_SOC_DAPM_INPUT("IN3R"),
+       SND_SOC_DAPM_INPUT("IN3L"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+
+       /* left playback mixer */
+       {"Left Playback Mixer", "Playback Switch", "Left DAC"},
+       {"Left Playback Mixer", "Left Bypass Switch", "IN3L PGA"},
+       {"Left Playback Mixer", "Right Playback Switch", "Right DAC"},
+       {"Left Playback Mixer", "Left Sidetone Switch", "Left Mic Mixer"},
+       {"Left Playback Mixer", "Right Sidetone Switch", "Right Mic Mixer"},
+
+       /* right playback mixer */
+       {"Right Playback Mixer", "Playback Switch", "Right DAC"},
+       {"Right Playback Mixer", "Right Bypass Switch", "IN3R PGA"},
+       {"Right Playback Mixer", "Left Playback Switch", "Left DAC"},
+       {"Right Playback Mixer", "Left Sidetone Switch", "Left Mic Mixer"},
+       {"Right Playback Mixer", "Right Sidetone Switch", "Right Mic Mixer"},
+
+       /* out4 playback mixer */
+       {"Out4 Mixer", "Right Playback Switch", "Right DAC"},
+       {"Out4 Mixer", "Left Playback Switch", "Left DAC"},
+       {"Out4 Mixer", "Right Capture Switch", "Right Capture Mixer"},
+       {"Out4 Mixer", "Out3 Playback Switch", "Out3 Mixer"},
+       {"Out4 Mixer", "Right Mixer Switch", "Right Playback Mixer"},
+       {"Out4 Mixer", "Left Mixer Switch", "Left Playback Mixer"},
+       {"OUT4", NULL, "Out4 Mixer"},
+
+       /* out3 playback mixer */
+       {"Out3 Mixer", "Left Playback Switch", "Left DAC"},
+       {"Out3 Mixer", "Left Capture Switch", "Left Capture Mixer"},
+       {"Out3 Mixer", "Left Mixer Switch", "Left Playback Mixer"},
+       {"Out3 Mixer", "Out4 Playback Switch", "Out4 Mixer"},
+       {"OUT3", NULL, "Out3 Mixer"},
+
+       /* out2 */
+       {"Right Out2 PGA", NULL, "Right Playback Mixer"},
+       {"Left Out2 PGA", NULL, "Left Playback Mixer"},
+       {"OUT2L", NULL, "Left Out2 PGA"},
+       {"OUT2R", NULL, "Right Out2 PGA"},
+
+       /* out1 */
+       {"Right Out1 PGA", NULL, "Right Playback Mixer"},
+       {"Left Out1 PGA", NULL, "Left Playback Mixer"},
+       {"OUT1L", NULL, "Left Out1 PGA"},
+       {"OUT1R", NULL, "Right Out1 PGA"},
+
+       /* ADCs */
+       {"Left ADC", NULL, "Left Capture Mixer"},
+       {"Right ADC", NULL, "Right Capture Mixer"},
+
+       /* Left capture mixer */
+       {"Left Capture Mixer", "L2 Capture Volume", "IN2L"},
+       {"Left Capture Mixer", "L3 Capture Volume", "IN3L PGA"},
+       {"Left Capture Mixer", "PGA Capture Switch", "Left Mic Mixer"},
+       {"Left Capture Mixer", NULL, "Out4 Capture Channel"},
+
+       /* Right capture mixer */
+       {"Right Capture Mixer", "L2 Capture Volume", "IN2R"},
+       {"Right Capture Mixer", "L3 Capture Volume", "IN3R PGA"},
+       {"Right Capture Mixer", "PGA Capture Switch", "Right Mic Mixer"},
+       {"Right Capture Mixer", NULL, "Out4 Capture Channel"},
+
+       /* L3 Inputs */
+       {"IN3L PGA", NULL, "IN3L"},
+       {"IN3R PGA", NULL, "IN3R"},
+
+       /* Left Mic mixer */
+       {"Left Mic Mixer", "INN Capture Switch", "IN1LN"},
+       {"Left Mic Mixer", "INP Capture Switch", "IN1LP"},
+       {"Left Mic Mixer", "IN2 Capture Switch", "IN2L"},
+
+       /* Right Mic mixer */
+       {"Right Mic Mixer", "INN Capture Switch", "IN1RN"},
+       {"Right Mic Mixer", "INP Capture Switch", "IN1RP"},
+       {"Right Mic Mixer", "IN2 Capture Switch", "IN2R"},
+
+       /* out 4 capture */
+       {"Out4 Capture Channel", NULL, "Out4 Mixer"},
+
+       /* Beep */
+       {"Beep", NULL, "IN3R PGA"},
+};
+
+static int wm8350_add_controls(struct snd_soc_codec *codec)
+{
+       int err, i;
+
+       for (i = 0; i < ARRAY_SIZE(wm8350_snd_controls); i++) {
+               err = snd_ctl_add(codec->card,
+                                 snd_soc_cnew(&wm8350_snd_controls[i],
+                                              codec, NULL));
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int wm8350_add_widgets(struct snd_soc_codec *codec)
+{
+       int ret;
+
+       ret = snd_soc_dapm_new_controls(codec,
+                                       wm8350_dapm_widgets,
+                                       ARRAY_SIZE(wm8350_dapm_widgets));
+       if (ret != 0) {
+               dev_err(codec->dev, "dapm control register failed\n");
+               return ret;
+       }
+
+       /* set up audio paths */
+       ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+       if (ret != 0) {
+               dev_err(codec->dev, "DAPM route register failed\n");
+               return ret;
+       }
+
+       return snd_soc_dapm_new_widgets(codec);
+}
+
+static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+                                int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct wm8350 *wm8350 = codec->control_data;
+       u16 fll_4;
+
+       switch (clk_id) {
+       case WM8350_MCLK_SEL_MCLK:
+               wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_1,
+                                 WM8350_MCLK_SEL);
+               break;
+       case WM8350_MCLK_SEL_PLL_MCLK:
+       case WM8350_MCLK_SEL_PLL_DAC:
+       case WM8350_MCLK_SEL_PLL_ADC:
+       case WM8350_MCLK_SEL_PLL_32K:
+               wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_1,
+                               WM8350_MCLK_SEL);
+               fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) &
+                   ~WM8350_FLL_CLK_SRC_MASK;
+               wm8350_codec_write(codec, WM8350_FLL_CONTROL_4, fll_4 | clk_id);
+               break;
+       }
+
+       /* MCLK direction */
+       if (dir == WM8350_MCLK_DIR_OUT)
+               wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+                               WM8350_MCLK_DIR);
+       else
+               wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+                                 WM8350_MCLK_DIR);
+
+       return 0;
+}
+
+static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 val;
+
+       switch (div_id) {
+       case WM8350_ADC_CLKDIV:
+               val = wm8350_codec_read(codec, WM8350_ADC_DIVIDER) &
+                   ~WM8350_ADC_CLKDIV_MASK;
+               wm8350_codec_write(codec, WM8350_ADC_DIVIDER, val | div);
+               break;
+       case WM8350_DAC_CLKDIV:
+               val = wm8350_codec_read(codec, WM8350_DAC_CLOCK_CONTROL) &
+                   ~WM8350_DAC_CLKDIV_MASK;
+               wm8350_codec_write(codec, WM8350_DAC_CLOCK_CONTROL, val | div);
+               break;
+       case WM8350_BCLK_CLKDIV:
+               val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+                   ~WM8350_BCLK_DIV_MASK;
+               wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+               break;
+       case WM8350_OPCLK_CLKDIV:
+               val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+                   ~WM8350_OPCLK_DIV_MASK;
+               wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+               break;
+       case WM8350_SYS_CLKDIV:
+               val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+                   ~WM8350_MCLK_DIV_MASK;
+               wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+               break;
+       case WM8350_DACLR_CLKDIV:
+               val = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) &
+                   ~WM8350_DACLRC_RATE_MASK;
+               wm8350_codec_write(codec, WM8350_DAC_LR_RATE, val | div);
+               break;
+       case WM8350_ADCLR_CLKDIV:
+               val = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) &
+                   ~WM8350_ADCLRC_RATE_MASK;
+               wm8350_codec_write(codec, WM8350_ADC_LR_RATE, val | div);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
+           ~(WM8350_AIF_BCLK_INV | WM8350_AIF_LRCLK_INV | WM8350_AIF_FMT_MASK);
+       u16 master = wm8350_codec_read(codec, WM8350_AI_DAC_CONTROL) &
+           ~WM8350_BCLK_MSTR;
+       u16 dac_lrc = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) &
+           ~WM8350_DACLRC_ENA;
+       u16 adc_lrc = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) &
+           ~WM8350_ADCLRC_ENA;
+
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               master |= WM8350_BCLK_MSTR;
+               dac_lrc |= WM8350_DACLRC_ENA;
+               adc_lrc |= WM8350_ADCLRC_ENA;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* interface format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               iface |= 0x2 << 8;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               iface |= 0x1 << 8;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               iface |= 0x3 << 8;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               iface |= 0x3 << 8;      /* lg not sure which mode */
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* clock inversion */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               iface |= WM8350_AIF_LRCLK_INV | WM8350_AIF_BCLK_INV;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               iface |= WM8350_AIF_BCLK_INV;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               iface |= WM8350_AIF_LRCLK_INV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
+       wm8350_codec_write(codec, WM8350_AI_DAC_CONTROL, master);
+       wm8350_codec_write(codec, WM8350_DAC_LR_RATE, dac_lrc);
+       wm8350_codec_write(codec, WM8350_ADC_LR_RATE, adc_lrc);
+       return 0;
+}
+
+static int wm8350_pcm_trigger(struct snd_pcm_substream *substream,
+                             int cmd, struct snd_soc_dai *codec_dai)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       int master = wm8350_codec_cache_read(codec, WM8350_AI_DAC_CONTROL) &
+           WM8350_BCLK_MSTR;
+       int enabled = 0;
+
+       /* Check that the DACs or ADCs are enabled since they are
+        * required for LRC in master mode. The DACs or ADCs need a
+        * valid audio path i.e. pin -> ADC or DAC -> pin before
+        * the LRC will be enabled in master mode. */
+       if (!master && cmd != SNDRV_PCM_TRIGGER_START)
+               return 0;
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               enabled = wm8350_codec_cache_read(codec, WM8350_POWER_MGMT_4) &
+                   (WM8350_ADCR_ENA | WM8350_ADCL_ENA);
+       } else {
+               enabled = wm8350_codec_cache_read(codec, WM8350_POWER_MGMT_4) &
+                   (WM8350_DACR_ENA | WM8350_DACL_ENA);
+       }
+
+       if (!enabled) {
+               dev_err(codec->dev,
+                      "%s: invalid audio path - no clocks available\n",
+                      __func__);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *codec_dai)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
+           ~WM8350_AIF_WL_MASK;
+
+       /* bit size */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               iface |= 0x1 << 10;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               iface |= 0x2 << 10;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               iface |= 0x3 << 10;
+               break;
+       }
+
+       wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
+       return 0;
+}
+
+static int wm8350_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm8350 *wm8350 = codec->control_data;
+
+       if (mute)
+               wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+       else
+               wm8350_clear_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+       return 0;
+}
+
+/* FLL divisors */
+struct _fll_div {
+       int div;                /* FLL_OUTDIV */
+       int n;
+       int k;
+       int ratio;              /* FLL_FRATIO */
+};
+
+/* The size in bits of the fll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static inline int fll_factors(struct _fll_div *fll_div, unsigned int input,
+                             unsigned int output)
+{
+       u64 Kpart;
+       unsigned int t1, t2, K, Nmod;
+
+       if (output >= 2815250 && output <= 3125000)
+               fll_div->div = 0x4;
+       else if (output >= 5625000 && output <= 6250000)
+               fll_div->div = 0x3;
+       else if (output >= 11250000 && output <= 12500000)
+               fll_div->div = 0x2;
+       else if (output >= 22500000 && output <= 25000000)
+               fll_div->div = 0x1;
+       else {
+               printk(KERN_ERR "wm8350: fll freq %d out of range\n", output);
+               return -EINVAL;
+       }
+
+       if (input > 48000)
+               fll_div->ratio = 1;
+       else
+               fll_div->ratio = 8;
+
+       t1 = output * (1 << (fll_div->div + 1));
+       t2 = input * fll_div->ratio;
+
+       fll_div->n = t1 / t2;
+       Nmod = t1 % t2;
+
+       if (Nmod) {
+               Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+               do_div(Kpart, t2);
+               K = Kpart & 0xFFFFFFFF;
+
+               /* Check if we need to round */
+               if ((K % 10) >= 5)
+                       K += 5;
+
+               /* Move down to proper range now rounding is done */
+               K /= 10;
+               fll_div->k = K;
+       } else
+               fll_div->k = 0;
+
+       return 0;
+}
+
+static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
+                         int pll_id, unsigned int freq_in,
+                         unsigned int freq_out)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct wm8350 *wm8350 = codec->control_data;
+       struct _fll_div fll_div;
+       int ret = 0;
+       u16 fll_1, fll_4;
+
+       /* power down FLL - we need to do this for reconfiguration */
+       wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4,
+                         WM8350_FLL_ENA | WM8350_FLL_OSC_ENA);
+
+       if (freq_out == 0 || freq_in == 0)
+               return ret;
+
+       ret = fll_factors(&fll_div, freq_in, freq_out);
+       if (ret < 0)
+               return ret;
+       dev_dbg(wm8350->dev,
+               "FLL in %d FLL out %d N 0x%x K 0x%x div %d ratio %d",
+               freq_in, freq_out, fll_div.n, fll_div.k, fll_div.div,
+               fll_div.ratio);
+
+       /* set up N.K & dividers */
+       fll_1 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_1) &
+           ~(WM8350_FLL_OUTDIV_MASK | WM8350_FLL_RSP_RATE_MASK | 0xc000);
+       wm8350_codec_write(codec, WM8350_FLL_CONTROL_1,
+                          fll_1 | (fll_div.div << 8) | 0x50);
+       wm8350_codec_write(codec, WM8350_FLL_CONTROL_2,
+                          (fll_div.ratio << 11) | (fll_div.
+                                                   n & WM8350_FLL_N_MASK));
+       wm8350_codec_write(codec, WM8350_FLL_CONTROL_3, fll_div.k);
+       fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) &
+           ~(WM8350_FLL_FRAC | WM8350_FLL_SLOW_LOCK_REF);
+       wm8350_codec_write(codec, WM8350_FLL_CONTROL_4,
+                          fll_4 | (fll_div.k ? WM8350_FLL_FRAC : 0) |
+                          (fll_div.ratio == 8 ? WM8350_FLL_SLOW_LOCK_REF : 0));
+
+       /* power FLL on */
+       wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_OSC_ENA);
+       wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_ENA);
+
+       return 0;
+}
+
+static int wm8350_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       struct wm8350 *wm8350 = codec->control_data;
+       struct wm8350_data *priv = codec->private_data;
+       struct wm8350_audio_platform_data *platform =
+               wm8350->codec.platform_data;
+       u16 pm1;
+       int ret;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+                   ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
+               wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+                                pm1 | WM8350_VMID_50K |
+                                platform->codec_current_on << 14);
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1);
+               pm1 &= ~WM8350_VMID_MASK;
+               wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+                                pm1 | WM8350_VMID_50K);
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->bias_level == SND_SOC_BIAS_OFF) {
+                       ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
+                                                   priv->supplies);
+                       if (ret != 0)
+                               return ret;
+
+                       /* Enable the system clock */
+                       wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4,
+                                       WM8350_SYSCLK_ENA);
+
+                       /* mute DAC & outputs */
+                       wm8350_set_bits(wm8350, WM8350_DAC_MUTE,
+                                       WM8350_DAC_MUTE_ENA);
+
+                       /* discharge cap memory */
+                       wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+                                        platform->dis_out1 |
+                                        (platform->dis_out2 << 2) |
+                                        (platform->dis_out3 << 4) |
+                                        (platform->dis_out4 << 6));
+
+                       /* wait for discharge */
+                       schedule_timeout_interruptible(msecs_to_jiffies
+                                                      (platform->
+                                                       cap_discharge_msecs));
+
+                       /* enable antipop */
+                       wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+                                        (platform->vmid_s_curve << 8));
+
+                       /* ramp up vmid */
+                       wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+                                        (platform->
+                                         codec_current_charge << 14) |
+                                        WM8350_VMID_5K | WM8350_VMIDEN |
+                                        WM8350_VBUFEN);
+
+                       /* wait for vmid */
+                       schedule_timeout_interruptible(msecs_to_jiffies
+                                                      (platform->
+                                                       vmid_charge_msecs));
+
+                       /* turn on vmid 300k  */
+                       pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+                           ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
+                       pm1 |= WM8350_VMID_300K |
+                               (platform->codec_current_standby << 14);
+                       wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+                                        pm1);
+
+
+                       /* enable analogue bias */
+                       pm1 |= WM8350_BIASEN;
+                       wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
+
+                       /* disable antipop */
+                       wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0);
+
+               } else {
+                       /* turn on vmid 300k and reduce current */
+                       pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+                           ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
+                       wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+                                        pm1 | WM8350_VMID_300K |
+                                        (platform->
+                                         codec_current_standby << 14));
+
+               }
+               break;
+
+       case SND_SOC_BIAS_OFF:
+
+               /* mute DAC & enable outputs */
+               wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+
+               wm8350_set_bits(wm8350, WM8350_POWER_MGMT_3,
+                               WM8350_OUT1L_ENA | WM8350_OUT1R_ENA |
+                               WM8350_OUT2L_ENA | WM8350_OUT2R_ENA);
+
+               /* enable anti pop S curve */
+               wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+                                (platform->vmid_s_curve << 8));
+
+               /* turn off vmid  */
+               pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+                   ~WM8350_VMIDEN;
+               wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
+
+               /* wait */
+               schedule_timeout_interruptible(msecs_to_jiffies
+                                              (platform->
+                                               vmid_discharge_msecs));
+
+               wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+                                (platform->vmid_s_curve << 8) |
+                                platform->dis_out1 |
+                                (platform->dis_out2 << 2) |
+                                (platform->dis_out3 << 4) |
+                                (platform->dis_out4 << 6));
+
+               /* turn off VBuf and drain */
+               pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+                   ~(WM8350_VBUFEN | WM8350_VMID_MASK);
+               wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+                                pm1 | WM8350_OUTPUT_DRAIN_EN);
+
+               /* wait */
+               schedule_timeout_interruptible(msecs_to_jiffies
+                                              (platform->drain_msecs));
+
+               pm1 &= ~WM8350_BIASEN;
+               wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
+
+               /* disable anti-pop */
+               wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0);
+
+               wm8350_clear_bits(wm8350, WM8350_LOUT1_VOLUME,
+                                 WM8350_OUT1L_ENA);
+               wm8350_clear_bits(wm8350, WM8350_ROUT1_VOLUME,
+                                 WM8350_OUT1R_ENA);
+               wm8350_clear_bits(wm8350, WM8350_LOUT2_VOLUME,
+                                 WM8350_OUT2L_ENA);
+               wm8350_clear_bits(wm8350, WM8350_ROUT2_VOLUME,
+                                 WM8350_OUT2R_ENA);
+
+               /* disable clock gen */
+               wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4,
+                                 WM8350_SYSCLK_ENA);
+
+               regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+                                      priv->supplies);
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+static int wm8350_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->codec;
+
+       wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int wm8350_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->codec;
+
+       wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+               wm8350_set_bias_level(codec, SND_SOC_BIAS_ON);
+
+       return 0;
+}
+
+static struct snd_soc_codec *wm8350_codec;
+
+static int wm8350_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       struct wm8350 *wm8350;
+       struct wm8350_data *priv;
+       int ret;
+       struct wm8350_output *out1;
+       struct wm8350_output *out2;
+
+       BUG_ON(!wm8350_codec);
+
+       socdev->codec = wm8350_codec;
+       codec = socdev->codec;
+       wm8350 = codec->control_data;
+       priv = codec->private_data;
+
+       /* Enable the codec */
+       wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
+
+       /* Enable robust clocking mode in ADC */
+       wm8350_codec_write(codec, WM8350_SECURITY, 0xa7);
+       wm8350_codec_write(codec, 0xde, 0x13);
+       wm8350_codec_write(codec, WM8350_SECURITY, 0);
+
+       /* read OUT1 & OUT2 volumes */
+       out1 = &priv->out1;
+       out2 = &priv->out2;
+       out1->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME) &
+                         WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+       out1->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME) &
+                          WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+       out2->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME) &
+                         WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+       out2->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME) &
+                          WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+       wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME, 0);
+       wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME, 0);
+       wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME, 0);
+       wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME, 0);
+
+       /* Latch VU bits & mute */
+       wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME,
+                       WM8350_OUT1_VU | WM8350_OUT1L_MUTE);
+       wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME,
+                       WM8350_OUT2_VU | WM8350_OUT2L_MUTE);
+       wm8350_set_bits(wm8350, WM8350_ROUT1_VOLUME,
+                       WM8350_OUT1_VU | WM8350_OUT1R_MUTE);
+       wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME,
+                       WM8350_OUT2_VU | WM8350_OUT2R_MUTE);
+
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to create pcms\n");
+               return ret;
+       }
+
+       wm8350_add_controls(codec);
+       wm8350_add_widgets(codec);
+
+       wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to register card\n");
+               goto card_err;
+       }
+
+       return 0;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+       return ret;
+}
+
+static int wm8350_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->codec;
+       struct wm8350 *wm8350 = codec->control_data;
+       int ret;
+
+       /* cancel any work waiting to be queued. */
+       ret = cancel_delayed_work(&codec->delayed_work);
+
+       /* if there was any work waiting then we run it now and
+        * wait for its completion */
+       if (ret) {
+               schedule_delayed_work(&codec->delayed_work, 0);
+               flush_scheduled_work();
+       }
+
+       wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
+
+       return 0;
+}
+
+#define WM8350_RATES (SNDRV_PCM_RATE_8000_96000)
+
+#define WM8350_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+                       SNDRV_PCM_FMTBIT_S20_3LE |\
+                       SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai wm8350_dai = {
+       .name = "WM8350",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = WM8350_RATES,
+               .formats = WM8350_FORMATS,
+       },
+       .capture = {
+                .stream_name = "Capture",
+                .channels_min = 1,
+                .channels_max = 2,
+                .rates = WM8350_RATES,
+                .formats = WM8350_FORMATS,
+        },
+       .ops = {
+                .hw_params = wm8350_pcm_hw_params,
+                .digital_mute = wm8350_mute,
+                .trigger = wm8350_pcm_trigger,
+                .set_fmt = wm8350_set_dai_fmt,
+                .set_sysclk = wm8350_set_dai_sysclk,
+                .set_pll = wm8350_set_fll,
+                .set_clkdiv = wm8350_set_clkdiv,
+        },
+};
+EXPORT_SYMBOL_GPL(wm8350_dai);
+
+struct snd_soc_codec_device soc_codec_dev_wm8350 = {
+       .probe =        wm8350_probe,
+       .remove =       wm8350_remove,
+       .suspend =      wm8350_suspend,
+       .resume =       wm8350_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8350);
+
+static int wm8350_codec_probe(struct platform_device *pdev)
+{
+       struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+       struct wm8350_data *priv;
+       struct snd_soc_codec *codec;
+       int ret, i;
+
+       if (wm8350->codec.platform_data == NULL) {
+               dev_err(&pdev->dev, "No audio platform data supplied\n");
+               return -EINVAL;
+       }
+
+       priv = kzalloc(sizeof(struct wm8350_data), GFP_KERNEL);
+       if (priv == NULL)
+               return -ENOMEM;
+
+       for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+               priv->supplies[i].supply = supply_names[i];
+
+       ret = regulator_bulk_get(wm8350->dev, ARRAY_SIZE(priv->supplies),
+                                priv->supplies);
+       if (ret != 0)
+               goto err_priv;
+
+       codec = &priv->codec;
+       wm8350->codec.codec = codec;
+
+       wm8350_dai.dev = &pdev->dev;
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+       codec->dev = &pdev->dev;
+       codec->name = "WM8350";
+       codec->owner = THIS_MODULE;
+       codec->read = wm8350_codec_read;
+       codec->write = wm8350_codec_write;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = wm8350_set_bias_level;
+       codec->dai = &wm8350_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = WM8350_MAX_REGISTER;
+       codec->private_data = priv;
+       codec->control_data = wm8350;
+
+       /* Put the codec into reset if it wasn't already */
+       wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
+
+       INIT_DELAYED_WORK(&codec->delayed_work, wm8350_pga_work);
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0)
+               goto err_supply;
+
+       wm8350_codec = codec;
+
+       ret = snd_soc_register_dai(&wm8350_dai);
+       if (ret != 0)
+               goto err_codec;
+       return 0;
+
+err_codec:
+       snd_soc_unregister_codec(codec);
+err_supply:
+       regulator_bulk_free(ARRAY_SIZE(priv->supplies), priv->supplies);
+err_priv:
+       kfree(priv);
+       wm8350_codec = NULL;
+       return ret;
+}
+
+static int __devexit wm8350_codec_remove(struct platform_device *pdev)
+{
+       struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = wm8350->codec.codec;
+       struct wm8350_data *priv = codec->private_data;
+
+       snd_soc_unregister_dai(&wm8350_dai);
+       snd_soc_unregister_codec(codec);
+       regulator_bulk_free(ARRAY_SIZE(priv->supplies), priv->supplies);
+       kfree(priv);
+       wm8350_codec = NULL;
+       return 0;
+}
+
+static struct platform_driver wm8350_codec_driver = {
+       .driver = {
+                  .name = "wm8350-codec",
+                  .owner = THIS_MODULE,
+                  },
+       .probe = wm8350_codec_probe,
+       .remove = __devexit_p(wm8350_codec_remove),
+};
+
+static __init int wm8350_init(void)
+{
+       return platform_driver_register(&wm8350_codec_driver);
+}
+module_init(wm8350_init);
+
+static __exit void wm8350_exit(void)
+{
+       platform_driver_unregister(&wm8350_codec_driver);
+}
+module_exit(wm8350_exit);
+
+MODULE_DESCRIPTION("ASoC WM8350 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8350-codec");
diff --git a/sound/soc/codecs/wm8350.h b/sound/soc/codecs/wm8350.h
new file mode 100644 (file)
index 0000000..cc2887a
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * wm8350.h - WM8903 audio codec interface
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#ifndef _WM8350_H
+#define _WM8350_H
+
+#include <sound/soc.h>
+
+extern struct snd_soc_dai wm8350_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8350;
+
+#endif
index d8ca2da..40f8238 100644 (file)
@@ -463,7 +463,8 @@ static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai,
 }
 
 static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -585,8 +586,6 @@ struct snd_soc_dai wm8510_dai = {
                .formats = WM8510_FORMATS,},
        .ops = {
                .hw_params = wm8510_pcm_hw_params,
-       },
-       .dai_ops = {
                .digital_mute = wm8510_mute,
                .set_fmt = wm8510_set_dai_fmt,
                .set_clkdiv = wm8510_set_dai_clkdiv,
@@ -659,7 +658,7 @@ static int wm8510_init(struct snd_soc_device *socdev)
        wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
        wm8510_add_controls(codec);
        wm8510_add_widgets(codec);
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                printk(KERN_ERR "wm8510: failed to register card\n");
                goto card_err;
@@ -890,6 +889,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8510 = {
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8510);
 
+static int __init wm8510_modinit(void)
+{
+       return snd_soc_register_dai(&wm8510_dai);
+}
+module_init(wm8510_modinit);
+
+static void __exit wm8510_exit(void)
+{
+       snd_soc_unregister_dai(&wm8510_dai);
+}
+module_exit(wm8510_exit);
+
 MODULE_DESCRIPTION("ASoC WM8510 driver");
 MODULE_AUTHOR("Liam Girdwood");
 MODULE_LICENSE("GPL");
index 627ebfb..d004e58 100644 (file)
@@ -548,13 +548,13 @@ static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
  * Set PCM DAI bit size and sample rate.
  */
 static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_dai_link *dai = rtd->dai;
        struct snd_soc_device *socdev = rtd->socdev;
        struct snd_soc_codec *codec = socdev->codec;
-       u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->codec_dai->id);
+       u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->id);
 
        paifb &= ~WM8580_AIF_LENGTH_MASK;
        /* bit size */
@@ -574,7 +574,7 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       wm8580_write(codec, WM8580_PAIF3 + dai->codec_dai->id, paifb);
+       wm8580_write(codec, WM8580_PAIF3 + dai->id, paifb);
        return 0;
 }
 
@@ -798,8 +798,6 @@ struct snd_soc_dai wm8580_dai[] = {
                },
                .ops = {
                         .hw_params = wm8580_paif_hw_params,
-                },
-               .dai_ops = {
                         .set_fmt = wm8580_set_paif_dai_fmt,
                         .set_clkdiv = wm8580_set_dai_clkdiv,
                         .set_pll = wm8580_set_dai_pll,
@@ -818,8 +816,6 @@ struct snd_soc_dai wm8580_dai[] = {
                },
                .ops = {
                         .hw_params = wm8580_paif_hw_params,
-                },
-               .dai_ops = {
                         .set_fmt = wm8580_set_paif_dai_fmt,
                         .set_clkdiv = wm8580_set_dai_clkdiv,
                         .set_pll = wm8580_set_dai_pll,
@@ -873,7 +869,7 @@ static int wm8580_init(struct snd_soc_device *socdev)
        wm8580_add_controls(codec);
        wm8580_add_widgets(codec);
 
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                printk(KERN_ERR "wm8580: failed to register card\n");
                goto card_err;
@@ -900,85 +896,85 @@ static struct snd_soc_device *wm8580_socdev;
  *    low  = 0x1a
  *    high = 0x1b
  */
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
 
-static struct i2c_driver wm8580_i2c_driver;
-static struct i2c_client client_template;
-
-static int wm8580_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+static int wm8580_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
 {
        struct snd_soc_device *socdev = wm8580_socdev;
-       struct wm8580_setup_data *setup = socdev->codec_data;
        struct snd_soc_codec *codec = socdev->codec;
-       struct i2c_client *i2c;
        int ret;
 
-       if (addr != setup->i2c_address)
-               return -ENODEV;
-
-       client_template.adapter = adap;
-       client_template.addr = addr;
-
-       i2c =  kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
-       if (i2c == NULL) {
-               kfree(codec);
-               return -ENOMEM;
-       }
        i2c_set_clientdata(i2c, codec);
        codec->control_data = i2c;
 
-       ret = i2c_attach_client(i2c);
-       if (ret < 0) {
-               dev_err(&i2c->dev, "failed to attach codec at addr %x\n", addr);
-               goto err;
-       }
-
        ret = wm8580_init(socdev);
-       if (ret < 0) {
+       if (ret < 0)
                dev_err(&i2c->dev, "failed to initialise WM8580\n");
-               goto err;
-       }
-
-       return ret;
-
-err:
-       kfree(codec);
-       kfree(i2c);
        return ret;
 }
 
-static int wm8580_i2c_detach(struct i2c_client *client)
+static int wm8580_i2c_remove(struct i2c_client *client)
 {
        struct snd_soc_codec *codec = i2c_get_clientdata(client);
-       i2c_detach_client(client);
        kfree(codec->reg_cache);
-       kfree(client);
        return 0;
 }
 
-static int wm8580_i2c_attach(struct i2c_adapter *adap)
-{
-       return i2c_probe(adap, &addr_data, wm8580_codec_probe);
-}
+static const struct i2c_device_id wm8580_i2c_id[] = {
+       { "wm8580", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id);
 
-/* corgi i2c codec control layer */
 static struct i2c_driver wm8580_i2c_driver = {
        .driver = {
                .name = "WM8580 I2C Codec",
                .owner = THIS_MODULE,
        },
-       .attach_adapter = wm8580_i2c_attach,
-       .detach_client =  wm8580_i2c_detach,
-       .command =        NULL,
+       .probe =    wm8580_i2c_probe,
+       .remove =   wm8580_i2c_remove,
+       .id_table = wm8580_i2c_id,
 };
 
-static struct i2c_client client_template = {
-       .name =   "WM8580",
-       .driver = &wm8580_i2c_driver,
-};
+static int wm8580_add_i2c_device(struct platform_device *pdev,
+                                const struct wm8580_setup_data *setup)
+{
+       struct i2c_board_info info;
+       struct i2c_adapter *adapter;
+       struct i2c_client *client;
+       int ret;
+
+       ret = i2c_add_driver(&wm8580_i2c_driver);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "can't add i2c driver\n");
+               return ret;
+       }
+
+       memset(&info, 0, sizeof(struct i2c_board_info));
+       info.addr = setup->i2c_address;
+       strlcpy(info.type, "wm8580", I2C_NAME_SIZE);
+
+       adapter = i2c_get_adapter(setup->i2c_bus);
+       if (!adapter) {
+               dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+                       setup->i2c_bus);
+               goto err_driver;
+       }
+
+       client = i2c_new_device(adapter, &info);
+       i2c_put_adapter(adapter);
+       if (!client) {
+               dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+                       (unsigned int)info.addr);
+               goto err_driver;
+       }
+
+       return 0;
+
+err_driver:
+       i2c_del_driver(&wm8580_i2c_driver);
+       return -ENODEV;
+}
 #endif
 
 static int wm8580_probe(struct platform_device *pdev)
@@ -1011,11 +1007,8 @@ static int wm8580_probe(struct platform_device *pdev)
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
        if (setup->i2c_address) {
-               normal_i2c[0] = setup->i2c_address;
                codec->hw_write = (hw_write_t)i2c_master_send;
-               ret = i2c_add_driver(&wm8580_i2c_driver);
-               if (ret != 0)
-                       printk(KERN_ERR "can't add i2c driver");
+               ret = wm8580_add_i2c_device(pdev, setup);
        }
 #else
                /* Add other interfaces here */
@@ -1034,6 +1027,7 @@ static int wm8580_remove(struct platform_device *pdev)
        snd_soc_free_pcms(socdev);
        snd_soc_dapm_free(socdev);
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       i2c_unregister_device(codec->control_data);
        i2c_del_driver(&wm8580_i2c_driver);
 #endif
        kfree(codec->private_data);
@@ -1048,6 +1042,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8580 = {
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);
 
+static int __init wm8580_modinit(void)
+{
+       return snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
+}
+module_init(wm8580_modinit);
+
+static void __exit wm8580_exit(void)
+{
+       snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
+}
+module_exit(wm8580_exit);
+
 MODULE_DESCRIPTION("ASoC WM8580 driver");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 MODULE_LICENSE("GPL");
index 589ddab..09e4422 100644 (file)
@@ -29,6 +29,7 @@
 #define WM8580_CLKSRC_NONE 5
 
 struct wm8580_setup_data {
+       int i2c_bus;
        unsigned short i2c_address;
 };
 
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
new file mode 100644 (file)
index 0000000..80b1198
--- /dev/null
@@ -0,0 +1,585 @@
+/*
+ * wm8728.c  --  WM8728 ALSA SoC Audio driver
+ *
+ * Copyright 2008 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8728.h"
+
+struct snd_soc_codec_device soc_codec_dev_wm8728;
+
+/*
+ * We can't read the WM8728 register space so we cache them instead.
+ * Note that the defaults here aren't the physical defaults, we latch
+ * the volume update bits, mute the output and enable infinite zero
+ * detect.
+ */
+static const u16 wm8728_reg_defaults[] = {
+       0x1ff,
+       0x1ff,
+       0x001,
+       0x100,
+};
+
+static inline unsigned int wm8728_read_reg_cache(struct snd_soc_codec *codec,
+       unsigned int reg)
+{
+       u16 *cache = codec->reg_cache;
+       BUG_ON(reg > ARRAY_SIZE(wm8728_reg_defaults));
+       return cache[reg];
+}
+
+static inline void wm8728_write_reg_cache(struct snd_soc_codec *codec,
+       u16 reg, unsigned int value)
+{
+       u16 *cache = codec->reg_cache;
+       BUG_ON(reg > ARRAY_SIZE(wm8728_reg_defaults));
+       cache[reg] = value;
+}
+
+/*
+ * write to the WM8728 register space
+ */
+static int wm8728_write(struct snd_soc_codec *codec, unsigned int reg,
+       unsigned int value)
+{
+       u8 data[2];
+
+       /* data is
+        *   D15..D9 WM8728 register offset
+        *   D8...D0 register data
+        */
+       data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+       data[1] = value & 0x00ff;
+
+       wm8728_write_reg_cache(codec, reg, value);
+
+       if (codec->hw_write(codec->control_data, data, 2) == 2)
+               return 0;
+       else
+               return -EIO;
+}
+
+static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1);
+
+static const struct snd_kcontrol_new wm8728_snd_controls[] = {
+
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8728_DACLVOL, WM8728_DACRVOL,
+                0, 255, 0, wm8728_tlv),
+
+SOC_SINGLE("Deemphasis", WM8728_DACCTL, 1, 1, 0),
+};
+
+static int wm8728_add_controls(struct snd_soc_codec *codec)
+{
+       int err, i;
+
+       for (i = 0; i < ARRAY_SIZE(wm8728_snd_controls); i++) {
+               err = snd_ctl_add(codec->card,
+                                 snd_soc_cnew(&wm8728_snd_controls[i],
+                                               codec, NULL));
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+/*
+ * DAPM controls.
+ */
+static const struct snd_soc_dapm_widget wm8728_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("VOUTL"),
+SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+       {"VOUTL", NULL, "DAC"},
+       {"VOUTR", NULL, "DAC"},
+};
+
+static int wm8728_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, wm8728_dapm_widgets,
+                                 ARRAY_SIZE(wm8728_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+       snd_soc_dapm_new_widgets(codec);
+
+       return 0;
+}
+
+static int wm8728_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u16 mute_reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+
+       if (mute)
+               wm8728_write(codec, WM8728_DACCTL, mute_reg | 1);
+       else
+               wm8728_write(codec, WM8728_DACCTL, mute_reg & ~1);
+
+       return 0;
+}
+
+static int wm8728_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params,
+       struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->codec;
+       u16 dac = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+
+       dac &= ~0x18;
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               dac |= 0x10;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               dac |= 0x08;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       wm8728_write(codec, WM8728_DACCTL, dac);
+
+       return 0;
+}
+
+static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 iface = wm8728_read_reg_cache(codec, WM8728_IFCTL);
+
+       /* Currently only I2S is supported by the driver, though the
+        * hardware is more flexible.
+        */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               iface |= 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* The hardware only support full slave mode */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               iface &= ~0x22;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               iface |=  0x20;
+               iface &= ~0x02;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               iface |= 0x02;
+               iface &= ~0x20;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               iface |= 0x22;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       wm8728_write(codec, WM8728_IFCTL, iface);
+       return 0;
+}
+
+static int wm8728_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       u16 reg;
+       int i;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+       case SND_SOC_BIAS_PREPARE:
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->bias_level == SND_SOC_BIAS_OFF) {
+                       /* Power everything up... */
+                       reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+                       wm8728_write(codec, WM8728_DACCTL, reg & ~0x4);
+
+                       /* ..then sync in the register cache. */
+                       for (i = 0; i < ARRAY_SIZE(wm8728_reg_defaults); i++)
+                               wm8728_write(codec, i,
+                                            wm8728_read_reg_cache(codec, i));
+               }
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+               wm8728_write(codec, WM8728_DACCTL, reg | 0x4);
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define WM8728_RATES (SNDRV_PCM_RATE_8000_192000)
+
+#define WM8728_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+       SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai wm8728_dai = {
+       .name = "WM8728",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = WM8728_RATES,
+               .formats = WM8728_FORMATS,
+       },
+       .ops = {
+                .hw_params = wm8728_hw_params,
+                .digital_mute = wm8728_mute,
+                .set_fmt = wm8728_set_dai_fmt,
+       }
+};
+EXPORT_SYMBOL_GPL(wm8728_dai);
+
+static int wm8728_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->codec;
+
+       wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int wm8728_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->codec;
+
+       wm8728_set_bias_level(codec, codec->suspend_bias_level);
+
+       return 0;
+}
+
+/*
+ * initialise the WM8728 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8728_init(struct snd_soc_device *socdev)
+{
+       struct snd_soc_codec *codec = socdev->codec;
+       int ret = 0;
+
+       codec->name = "WM8728";
+       codec->owner = THIS_MODULE;
+       codec->read = wm8728_read_reg_cache;
+       codec->write = wm8728_write;
+       codec->set_bias_level = wm8728_set_bias_level;
+       codec->dai = &wm8728_dai;
+       codec->num_dai = 1;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults);
+       codec->reg_cache = kmemdup(wm8728_reg_defaults,
+                                  sizeof(wm8728_reg_defaults),
+                                  GFP_KERNEL);
+       if (codec->reg_cache == NULL)
+               return -ENOMEM;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               printk(KERN_ERR "wm8728: failed to create pcms\n");
+               goto pcm_err;
+       }
+
+       /* power on device */
+       wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       wm8728_add_controls(codec);
+       wm8728_add_widgets(codec);
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               printk(KERN_ERR "wm8728: failed to register card\n");
+               goto card_err;
+       }
+
+       return ret;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+pcm_err:
+       kfree(codec->reg_cache);
+       return ret;
+}
+
+static struct snd_soc_device *wm8728_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+/*
+ * WM8728 2 wire address is determined by GPIO5
+ * state during powerup.
+ *    low  = 0x1a
+ *    high = 0x1b
+ */
+
+static int wm8728_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct snd_soc_device *socdev = wm8728_socdev;
+       struct snd_soc_codec *codec = socdev->codec;
+       int ret;
+
+       i2c_set_clientdata(i2c, codec);
+       codec->control_data = i2c;
+
+       ret = wm8728_init(socdev);
+       if (ret < 0)
+               pr_err("failed to initialise WM8728\n");
+
+       return ret;
+}
+
+static int wm8728_i2c_remove(struct i2c_client *client)
+{
+       struct snd_soc_codec *codec = i2c_get_clientdata(client);
+       kfree(codec->reg_cache);
+       return 0;
+}
+
+static const struct i2c_device_id wm8728_i2c_id[] = {
+       { "wm8728", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id);
+
+static struct i2c_driver wm8728_i2c_driver = {
+       .driver = {
+               .name = "WM8728 I2C Codec",
+               .owner = THIS_MODULE,
+       },
+       .probe =    wm8728_i2c_probe,
+       .remove =   wm8728_i2c_remove,
+       .id_table = wm8728_i2c_id,
+};
+
+static int wm8728_add_i2c_device(struct platform_device *pdev,
+                                const struct wm8728_setup_data *setup)
+{
+       struct i2c_board_info info;
+       struct i2c_adapter *adapter;
+       struct i2c_client *client;
+       int ret;
+
+       ret = i2c_add_driver(&wm8728_i2c_driver);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "can't add i2c driver\n");
+               return ret;
+       }
+
+       memset(&info, 0, sizeof(struct i2c_board_info));
+       info.addr = setup->i2c_address;
+       strlcpy(info.type, "wm8728", I2C_NAME_SIZE);
+
+       adapter = i2c_get_adapter(setup->i2c_bus);
+       if (!adapter) {
+               dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+                       setup->i2c_bus);
+               goto err_driver;
+       }
+
+       client = i2c_new_device(adapter, &info);
+       i2c_put_adapter(adapter);
+       if (!client) {
+               dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+                       (unsigned int)info.addr);
+               goto err_driver;
+       }
+
+       return 0;
+
+err_driver:
+       i2c_del_driver(&wm8728_i2c_driver);
+       return -ENODEV;
+}
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8728_spi_probe(struct spi_device *spi)
+{
+       struct snd_soc_device *socdev = wm8728_socdev;
+       struct snd_soc_codec *codec = socdev->codec;
+       int ret;
+
+       codec->control_data = spi;
+
+       ret = wm8728_init(socdev);
+       if (ret < 0)
+               dev_err(&spi->dev, "failed to initialise WM8728\n");
+
+       return ret;
+}
+
+static int __devexit wm8728_spi_remove(struct spi_device *spi)
+{
+       return 0;
+}
+
+static struct spi_driver wm8728_spi_driver = {
+       .driver = {
+               .name   = "wm8728",
+               .bus    = &spi_bus_type,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = wm8728_spi_probe,
+       .remove         = __devexit_p(wm8728_spi_remove),
+};
+
+static int wm8728_spi_write(struct spi_device *spi, const char *data, int len)
+{
+       struct spi_transfer t;
+       struct spi_message m;
+       u8 msg[2];
+
+       if (len <= 0)
+               return 0;
+
+       msg[0] = data[0];
+       msg[1] = data[1];
+
+       spi_message_init(&m);
+       memset(&t, 0, (sizeof t));
+
+       t.tx_buf = &msg[0];
+       t.len = len;
+
+       spi_message_add_tail(&t, &m);
+       spi_sync(spi, &m);
+
+       return len;
+}
+#endif /* CONFIG_SPI_MASTER */
+
+static int wm8728_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct wm8728_setup_data *setup;
+       struct snd_soc_codec *codec;
+       int ret = 0;
+
+       setup = socdev->codec_data;
+       codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+       if (codec == NULL)
+               return -ENOMEM;
+
+       socdev->codec = codec;
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       wm8728_socdev = socdev;
+       ret = -ENODEV;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       if (setup->i2c_address) {
+               codec->hw_write = (hw_write_t)i2c_master_send;
+               ret = wm8728_add_i2c_device(pdev, setup);
+       }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+       if (setup->spi) {
+               codec->hw_write = (hw_write_t)wm8728_spi_write;
+               ret = spi_register_driver(&wm8728_spi_driver);
+               if (ret != 0)
+                       printk(KERN_ERR "can't add spi driver");
+       }
+#endif
+
+       if (ret != 0)
+               kfree(codec);
+
+       return ret;
+}
+
+/* power down chip */
+static int wm8728_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->codec;
+
+       if (codec->control_data)
+               wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       i2c_unregister_device(codec->control_data);
+       i2c_del_driver(&wm8728_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+       spi_unregister_driver(&wm8728_spi_driver);
+#endif
+       kfree(codec);
+
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8728 = {
+       .probe =        wm8728_probe,
+       .remove =       wm8728_remove,
+       .suspend =      wm8728_suspend,
+       .resume =       wm8728_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8728);
+
+static int __init wm8728_modinit(void)
+{
+       return snd_soc_register_dai(&wm8728_dai);
+}
+module_init(wm8728_modinit);
+
+static void __exit wm8728_exit(void)
+{
+       snd_soc_unregister_dai(&wm8728_dai);
+}
+module_exit(wm8728_exit);
+
+MODULE_DESCRIPTION("ASoC WM8728 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8728.h b/sound/soc/codecs/wm8728.h
new file mode 100644 (file)
index 0000000..d269c13
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * wm8728.h  --  WM8728 ASoC codec driver
+ *
+ * Copyright 2008 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8728_H
+#define _WM8728_H
+
+#define WM8728_DACLVOL   0x00
+#define WM8728_DACRVOL   0x01
+#define WM8728_DACCTL    0x02
+#define WM8728_IFCTL     0x03
+
+struct wm8728_setup_data {
+       int            spi;
+       int            i2c_bus;
+       unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai wm8728_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8728;
+
+#endif
index 7f8a7e3..c444b9f 100644 (file)
@@ -264,7 +264,8 @@ static inline int get_coeff(int mclk, int rate)
 }
 
 static int wm8731_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -293,7 +294,8 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int wm8731_pcm_prepare(struct snd_pcm_substream *substream)
+static int wm8731_pcm_prepare(struct snd_pcm_substream *substream,
+                             struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -305,7 +307,8 @@ static int wm8731_pcm_prepare(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static void wm8731_shutdown(struct snd_pcm_substream *substream)
+static void wm8731_shutdown(struct snd_pcm_substream *substream,
+                           struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -461,8 +464,6 @@ struct snd_soc_dai wm8731_dai = {
                .prepare = wm8731_pcm_prepare,
                .hw_params = wm8731_hw_params,
                .shutdown = wm8731_shutdown,
-       },
-       .dai_ops = {
                .digital_mute = wm8731_mute,
                .set_sysclk = wm8731_set_dai_sysclk,
                .set_fmt = wm8731_set_dai_fmt,
@@ -544,7 +545,7 @@ static int wm8731_init(struct snd_soc_device *socdev)
 
        wm8731_add_controls(codec);
        wm8731_add_widgets(codec);
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                printk(KERN_ERR "wm8731: failed to register card\n");
                goto card_err;
@@ -792,6 +793,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8731 = {
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
 
+static int __init wm8731_modinit(void)
+{
+       return snd_soc_register_dai(&wm8731_dai);
+}
+module_init(wm8731_modinit);
+
+static void __exit wm8731_exit(void)
+{
+       snd_soc_unregister_dai(&wm8731_dai);
+}
+module_exit(wm8731_exit);
+
 MODULE_DESCRIPTION("ASoC WM8731 driver");
 MODULE_AUTHOR("Richard Purdie");
 MODULE_LICENSE("GPL");
index 9b7296e..5997fa6 100644 (file)
@@ -614,7 +614,8 @@ static int wm8750_set_dai_fmt(struct snd_soc_dai *codec_dai,
 }
 
 static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -709,8 +710,6 @@ struct snd_soc_dai wm8750_dai = {
                .formats = WM8750_FORMATS,},
        .ops = {
                .hw_params = wm8750_pcm_hw_params,
-       },
-       .dai_ops = {
                .digital_mute = wm8750_mute,
                .set_fmt = wm8750_set_dai_fmt,
                .set_sysclk = wm8750_set_dai_sysclk,
@@ -819,7 +818,7 @@ static int wm8750_init(struct snd_soc_device *socdev)
 
        wm8750_add_controls(codec);
        wm8750_add_widgets(codec);
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                printk(KERN_ERR "wm8750: failed to register card\n");
                goto card_err;
@@ -1086,6 +1085,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8750 = {
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8750);
 
+static int __init wm8750_modinit(void)
+{
+       return snd_soc_register_dai(&wm8750_dai);
+}
+module_init(wm8750_modinit);
+
+static void __exit wm8750_exit(void)
+{
+       snd_soc_unregister_dai(&wm8750_dai);
+}
+module_exit(wm8750_exit);
+
 MODULE_DESCRIPTION("ASoC WM8750 driver");
 MODULE_AUTHOR("Liam Girdwood");
 MODULE_LICENSE("GPL");
index d426eaa..6c21b50 100644 (file)
@@ -922,7 +922,8 @@ static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai,
  * Set PCM DAI bit size and sample rate.
  */
 static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -1155,7 +1156,8 @@ static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
  * Set PCM DAI bit size and sample rate.
  */
 static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -1323,16 +1325,15 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
                .channels_min = 1,
                .channels_max = 2,
                .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
+               .formats = WM8753_FORMATS},
        .capture = { /* dummy for fast DAI switching */
                .stream_name = "Capture",
                .channels_min = 1,
                .channels_max = 2,
                .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
+               .formats = WM8753_FORMATS},
        .ops = {
-               .hw_params = wm8753_i2s_hw_params,},
-       .dai_ops = {
+               .hw_params = wm8753_i2s_hw_params,
                .digital_mute = wm8753_mute,
                .set_fmt = wm8753_mode1h_set_dai_fmt,
                .set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1356,8 +1357,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
                .rates = WM8753_RATES,
                .formats = WM8753_FORMATS,},
        .ops = {
-               .hw_params = wm8753_pcm_hw_params,},
-       .dai_ops = {
+               .hw_params = wm8753_pcm_hw_params,
                .digital_mute = wm8753_mute,
                .set_fmt = wm8753_mode1v_set_dai_fmt,
                .set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1385,8 +1385,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
                .rates = WM8753_RATES,
                .formats = WM8753_FORMATS,},
        .ops = {
-               .hw_params = wm8753_pcm_hw_params,},
-       .dai_ops = {
+               .hw_params = wm8753_pcm_hw_params,
                .digital_mute = wm8753_mute,
                .set_fmt = wm8753_mode2_set_dai_fmt,
                .set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1410,8 +1409,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
                .rates = WM8753_RATES,
                .formats = WM8753_FORMATS,},
        .ops = {
-               .hw_params = wm8753_i2s_hw_params,},
-       .dai_ops = {
+               .hw_params = wm8753_i2s_hw_params,
                .digital_mute = wm8753_mute,
                .set_fmt = wm8753_mode3_4_set_dai_fmt,
                .set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1439,8 +1437,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
                .rates = WM8753_RATES,
                .formats = WM8753_FORMATS,},
        .ops = {
-               .hw_params = wm8753_i2s_hw_params,},
-       .dai_ops = {
+               .hw_params = wm8753_i2s_hw_params,
                .digital_mute = wm8753_mute,
                .set_fmt = wm8753_mode3_4_set_dai_fmt,
                .set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1608,7 +1605,7 @@ static int wm8753_init(struct snd_soc_device *socdev)
 
        wm8753_add_controls(codec);
        wm8753_add_widgets(codec);
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                printk(KERN_ERR "wm8753: failed to register card\n");
                goto card_err;
@@ -1877,6 +1874,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8753 = {
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8753);
 
+static int __init wm8753_modinit(void)
+{
+       return snd_soc_register_dais(wm8753_dai, ARRAY_SIZE(wm8753_dai));
+}
+module_init(wm8753_modinit);
+
+static void __exit wm8753_exit(void)
+{
+       snd_soc_unregister_dais(wm8753_dai, ARRAY_SIZE(wm8753_dai));
+}
+module_exit(wm8753_exit);
+
 MODULE_DESCRIPTION("ASoC WM8753 driver");
 MODULE_AUTHOR("Liam Girdwood");
 MODULE_LICENSE("GPL");
index 3b326c9..6767de1 100644 (file)
 struct snd_soc_codec_device soc_codec_dev_wm8900;
 
 struct wm8900_priv {
+       struct snd_soc_codec codec;
+
+       u16 reg_cache[WM8900_MAXREG];
+
        u32 fll_in; /* FLL input frequency */
        u32 fll_out; /* FLL output frequency */
 };
@@ -727,7 +731,8 @@ static int wm8900_add_widgets(struct snd_soc_codec *codec)
 }
 
 static int wm8900_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
+       struct snd_pcm_hw_params *params,
+       struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -1117,8 +1122,6 @@ struct snd_soc_dai wm8900_dai = {
         },
        .ops = {
                .hw_params = wm8900_hw_params,
-        },
-       .dai_ops = {
                 .set_clkdiv = wm8900_set_dai_clkdiv,
                 .set_pll = wm8900_set_dai_pll,
                 .set_fmt = wm8900_set_dai_fmt,
@@ -1283,16 +1286,28 @@ static int wm8900_resume(struct platform_device *pdev)
        return 0;
 }
 
-/*
- * initialise the WM8900 driver
- * register the mixer and dsp interfaces with the kernel
- */
-static int wm8900_init(struct snd_soc_device *socdev)
+static struct snd_soc_codec *wm8900_codec;
+
+static int wm8900_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
 {
-       struct snd_soc_codec *codec = socdev->codec;
-       int ret = 0;
+       struct wm8900_priv *wm8900;
+       struct snd_soc_codec *codec;
        unsigned int reg;
-       struct i2c_client *i2c_client = socdev->codec->control_data;
+       int ret;
+
+       wm8900 = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL);
+       if (wm8900 == NULL)
+               return -ENOMEM;
+
+       codec = &wm8900->codec;
+       codec->private_data = wm8900;
+       codec->reg_cache = &wm8900->reg_cache[0];
+       codec->reg_cache_size = WM8900_MAXREG;
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
 
        codec->name = "WM8900";
        codec->owner = THIS_MODULE;
@@ -1300,33 +1315,28 @@ static int wm8900_init(struct snd_soc_device *socdev)
        codec->write = wm8900_write;
        codec->dai = &wm8900_dai;
        codec->num_dai = 1;
-       codec->reg_cache_size = WM8900_MAXREG;
-       codec->reg_cache = kmemdup(wm8900_reg_defaults,
-                                  sizeof(wm8900_reg_defaults), GFP_KERNEL);
-
-       if (codec->reg_cache == NULL)
-               return -ENOMEM;
+       codec->hw_write = (hw_write_t)i2c_master_send;
+       codec->control_data = i2c;
+       codec->set_bias_level = wm8900_set_bias_level;
+       codec->dev = &i2c->dev;
 
        reg = wm8900_read(codec, WM8900_REG_ID);
        if (reg != 0x8900) {
-               dev_err(&i2c_client->dev, "Device is not a WM8900 - ID %x\n",
-                       reg);
-               return -ENODEV;
-       }
-
-       codec->private_data = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL);
-       if (codec->private_data == NULL) {
-               ret = -ENOMEM;
-               goto priv_err;
+               dev_err(&i2c->dev, "Device is not a WM8900 - ID %x\n", reg);
+               ret = -ENODEV;
+               goto err;
        }
 
        /* Read back from the chip */
        reg = wm8900_chip_read(codec, WM8900_REG_POWER1);
        reg = (reg >> 12) & 0xf;
-       dev_info(&i2c_client->dev, "WM8900 revision %d\n", reg);
+       dev_info(&i2c->dev, "WM8900 revision %d\n", reg);
 
        wm8900_reset(codec);
 
+       /* Turn the chip on */
+       wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
        /* Latch the volume update bits */
        wm8900_write(codec, WM8900_REG_LINVOL,
                     wm8900_read(codec, WM8900_REG_LINVOL) | 0x100);
@@ -1352,160 +1362,98 @@ static int wm8900_init(struct snd_soc_device *socdev)
        /* Set the DAC and mixer output bias */
        wm8900_write(codec, WM8900_REG_OUTBIASCTL, 0x81);
 
-       /* Register pcms */
-       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-       if (ret < 0) {
-               dev_err(&i2c_client->dev, "Failed to register new PCMs\n");
-               goto pcm_err;
-       }
-
-       /* Turn the chip on */
-       codec->bias_level = SND_SOC_BIAS_OFF;
-       wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       wm8900_add_controls(codec);
-       wm8900_add_widgets(codec);
-
-       ret = snd_soc_register_card(socdev);
-       if (ret < 0) {
-               dev_err(&i2c_client->dev, "Failed to register card\n");
-               goto card_err;
-       }
-       return ret;
-
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
-pcm_err:
-       kfree(codec->reg_cache);
-priv_err:
-       kfree(codec->private_data);
-       return ret;
-}
+       wm8900_dai.dev = &i2c->dev;
 
-static struct snd_soc_device *wm8900_socdev;
+       wm8900_codec = codec;
 
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
-
-static struct i2c_driver wm8900_i2c_driver;
-static struct i2c_client client_template;
-
-/* If the i2c layer weren't so broken, we could pass this kind of data
-   around */
-static int wm8900_codec_probe(struct i2c_adapter *adap, int addr, int kind)
-{
-       struct snd_soc_device *socdev = wm8900_socdev;
-       struct wm8900_setup_data *setup = socdev->codec_data;
-       struct snd_soc_codec *codec = socdev->codec;
-       struct i2c_client *i2c;
-       int ret;
-
-       if (addr != setup->i2c_address)
-               return -ENODEV;
-
-       dev_err(&adap->dev, "Probe on %x\n", addr);
-
-       client_template.adapter = adap;
-       client_template.addr = addr;
-
-       i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
-       if (i2c == NULL) {
-               kfree(codec);
-               return -ENOMEM;
-       }
-       i2c_set_clientdata(i2c, codec);
-       codec->control_data = i2c;
-
-       ret = i2c_attach_client(i2c);
-       if (ret < 0) {
-               dev_err(&adap->dev,
-                       "failed to attach codec at addr %x\n", addr);
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
                goto err;
        }
 
-       ret = wm8900_init(socdev);
-       if (ret < 0) {
-               dev_err(&adap->dev, "failed to initialise WM8900\n");
-               goto err;
+       ret = snd_soc_register_dai(&wm8900_dai);
+       if (ret != 0) {
+               dev_err(&i2c->dev, "Failed to register DAI: %d\n", ret);
+               goto err_codec;
        }
+
        return ret;
 
+err_codec:
+       snd_soc_unregister_codec(codec);
 err:
-       kfree(codec);
-       kfree(i2c);
+       kfree(wm8900);
+       wm8900_codec = NULL;
        return ret;
 }
 
-static int wm8900_i2c_detach(struct i2c_client *client)
+static int wm8900_i2c_remove(struct i2c_client *client)
 {
-       struct snd_soc_codec *codec = i2c_get_clientdata(client);
-       i2c_detach_client(client);
-       kfree(codec->reg_cache);
-       kfree(client);
+       snd_soc_unregister_dai(&wm8900_dai);
+       snd_soc_unregister_codec(wm8900_codec);
+
+       wm8900_set_bias_level(wm8900_codec, SND_SOC_BIAS_OFF);
+
+       wm8900_dai.dev = NULL;
+       kfree(wm8900_codec->private_data);
+       wm8900_codec = NULL;
+
        return 0;
 }
 
-static int wm8900_i2c_attach(struct i2c_adapter *adap)
-{
-       return i2c_probe(adap, &addr_data, wm8900_codec_probe);
-}
+static const struct i2c_device_id wm8900_i2c_id[] = {
+       { "wm8900", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8900_i2c_id);
 
-/* corgi i2c codec control layer */
 static struct i2c_driver wm8900_i2c_driver = {
        .driver = {
-               .name = "WM8900 I2C codec",
+               .name = "WM8900",
                .owner = THIS_MODULE,
        },
-       .attach_adapter = wm8900_i2c_attach,
-       .detach_client =  wm8900_i2c_detach,
-       .command =        NULL,
-};
-
-static struct i2c_client client_template = {
-       .name =   "WM8900",
-       .driver = &wm8900_i2c_driver,
+       .probe = wm8900_i2c_probe,
+       .remove = wm8900_i2c_remove,
+       .id_table = wm8900_i2c_id,
 };
-#endif
 
 static int wm8900_probe(struct platform_device *pdev)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct wm8900_setup_data *setup;
        struct snd_soc_codec *codec;
        int ret = 0;
 
-       dev_info(&pdev->dev, "WM8900 Audio Codec\n");
-
-       setup = socdev->codec_data;
-       codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-       if (codec == NULL)
-               return -ENOMEM;
-
-       mutex_init(&codec->mutex);
-       INIT_LIST_HEAD(&codec->dapm_widgets);
-       INIT_LIST_HEAD(&codec->dapm_paths);
+       if (!wm8900_codec) {
+               dev_err(&pdev->dev, "I2C client not yet instantiated\n");
+               return -ENODEV;
+       }
 
+       codec = wm8900_codec;
        socdev->codec = codec;
 
-       codec->set_bias_level = wm8900_set_bias_level;
+       /* Register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to register new PCMs\n");
+               goto pcm_err;
+       }
 
-       wm8900_socdev = socdev;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-       if (setup->i2c_address) {
-               normal_i2c[0] = setup->i2c_address;
-               codec->hw_write = (hw_write_t)i2c_master_send;
-               ret = i2c_add_driver(&wm8900_i2c_driver);
-               if (ret != 0)
-                       printk(KERN_ERR "can't add i2c driver");
+       wm8900_add_controls(codec);
+       wm8900_add_widgets(codec);
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to register card\n");
+               goto card_err;
        }
-#else
-#error Non-I2C interfaces not yet supported
-#endif
+
+       return ret;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+pcm_err:
        return ret;
 }
 
@@ -1513,17 +1461,9 @@ static int wm8900_probe(struct platform_device *pdev)
 static int wm8900_remove(struct platform_device *pdev)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_codec *codec = socdev->codec;
-
-       if (codec->control_data)
-               wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
        snd_soc_free_pcms(socdev);
        snd_soc_dapm_free(socdev);
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-       i2c_del_driver(&wm8900_i2c_driver);
-#endif
-       kfree(codec);
 
        return 0;
 }
@@ -1536,6 +1476,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8900 = {
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8900);
 
+static int __init wm8900_modinit(void)
+{
+       return i2c_add_driver(&wm8900_i2c_driver);
+}
+module_init(wm8900_modinit);
+
+static void __exit wm8900_exit(void)
+{
+       i2c_del_driver(&wm8900_i2c_driver);
+}
+module_exit(wm8900_exit);
+
 MODULE_DESCRIPTION("ASoC WM8900 driver");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfonmicro.com>");
 MODULE_LICENSE("GPL");
index ba450d9..fd15007 100644 (file)
 #define WM8900_DAC_CLKDIV_5_5 0x14
 #define WM8900_DAC_CLKDIV_6   0x18
 
-#define WM8900_
-
-struct wm8900_setup_data {
-       unsigned short i2c_address;
-};
-
 extern struct snd_soc_dai wm8900_dai;
 extern struct snd_soc_codec_device soc_codec_dev_wm8900;
 
index ce40d78..bde7454 100644 (file)
 
 #include "wm8903.h"
 
-struct wm8903_priv {
-       int sysclk;
-
-       /* Reference counts */
-       int charge_pump_users;
-       int class_w_users;
-       int playback_active;
-       int capture_active;
-
-       struct snd_pcm_substream *master_substream;
-       struct snd_pcm_substream *slave_substream;
-};
-
 /* Register defaults at reset */
 static u16 wm8903_reg_defaults[] = {
        0x8903,     /* R0   - SW Reset and ID */
@@ -223,6 +210,23 @@ static u16 wm8903_reg_defaults[] = {
        0x0000,     /* R172 - Analogue Output Bias 0 */
 };
 
+struct wm8903_priv {
+       struct snd_soc_codec codec;
+       u16 reg_cache[ARRAY_SIZE(wm8903_reg_defaults)];
+
+       int sysclk;
+
+       /* Reference counts */
+       int charge_pump_users;
+       int class_w_users;
+       int playback_active;
+       int capture_active;
+
+       struct snd_pcm_substream *master_substream;
+       struct snd_pcm_substream *slave_substream;
+};
+
+
 static unsigned int wm8903_read_reg_cache(struct snd_soc_codec *codec,
                                                 unsigned int reg)
 {
@@ -360,6 +364,8 @@ static void wm8903_sync_reg_cache(struct snd_soc_codec *codec, u16 *cache)
 static void wm8903_reset(struct snd_soc_codec *codec)
 {
        wm8903_write(codec, WM8903_SW_RESET_AND_ID, 0);
+       memcpy(codec->reg_cache, wm8903_reg_defaults,
+              sizeof(wm8903_reg_defaults));
 }
 
 #define WM8903_OUTPUT_SHORT 0x8
@@ -392,6 +398,7 @@ static int wm8903_output_event(struct snd_soc_dapm_widget *w,
                break;
        default:
                BUG();
+               return -EINVAL;  /* Spurious warning from some compilers */
        }
 
        switch (w->shift) {
@@ -403,6 +410,7 @@ static int wm8903_output_event(struct snd_soc_dapm_widget *w,
                break;
        default:
                BUG();
+               return -EINVAL;  /* Spurious warning from some compilers */
        }
 
        if (event & SND_SOC_DAPM_PRE_PMU) {
@@ -773,14 +781,14 @@ static const struct snd_kcontrol_new left_output_mixer[] = {
 SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0),
 SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0),
 SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0),
-SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0),
+SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 0, 1, 0),
 };
 
 static const struct snd_kcontrol_new right_output_mixer[] = {
 SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 3, 1, 0),
 SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 2, 1, 0),
 SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0),
-SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0),
+SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 0, 1, 0),
 };
 
 static const struct snd_kcontrol_new left_speaker_mixer[] = {
@@ -788,7 +796,7 @@ SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 3, 1, 0),
 SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 2, 1, 0),
 SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 1, 1, 0),
 SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0,
-               1, 1, 0),
+               0, 1, 0),
 };
 
 static const struct snd_kcontrol_new right_speaker_mixer[] = {
@@ -797,7 +805,7 @@ SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 2, 1, 0),
 SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0,
                1, 1, 0),
 SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0,
-               1, 1, 0),
+               0, 1, 0),
 };
 
 static const struct snd_soc_dapm_widget wm8903_dapm_widgets[] = {
@@ -989,6 +997,9 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
 
        case SND_SOC_BIAS_STANDBY:
                if (codec->bias_level == SND_SOC_BIAS_OFF) {
+                       wm8903_write(codec, WM8903_CLOCK_RATES_2,
+                                    WM8903_CLK_SYS_ENA);
+
                        wm8903_run_sequence(codec, 0);
                        wm8903_sync_reg_cache(codec, codec->reg_cache);
 
@@ -1019,6 +1030,9 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
 
        case SND_SOC_BIAS_OFF:
                wm8903_run_sequence(codec, 32);
+               reg = wm8903_read(codec, WM8903_CLOCK_RATES_2);
+               reg &= ~WM8903_CLK_SYS_ENA;
+               wm8903_write(codec, WM8903_CLOCK_RATES_2, reg);
                break;
        }
 
@@ -1257,7 +1271,8 @@ static struct {
        { 0,      0 },
 };
 
-static int wm8903_startup(struct snd_pcm_substream *substream)
+static int wm8903_startup(struct snd_pcm_substream *substream,
+                         struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -1298,7 +1313,8 @@ static int wm8903_startup(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static void wm8903_shutdown(struct snd_pcm_substream *substream)
+static void wm8903_shutdown(struct snd_pcm_substream *substream,
+                           struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -1317,7 +1333,8 @@ static void wm8903_shutdown(struct snd_pcm_substream *substream)
 }
 
 static int wm8903_hw_params(struct snd_pcm_substream *substream,
-                           struct snd_pcm_hw_params *params)
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -1515,8 +1532,6 @@ struct snd_soc_dai wm8903_dai = {
                 .startup = wm8903_startup,
                 .shutdown = wm8903_shutdown,
                 .hw_params = wm8903_hw_params,
-       },
-       .dai_ops = {
                 .digital_mute = wm8903_digital_mute,
                 .set_fmt = wm8903_set_dai_fmt,
                 .set_sysclk = wm8903_set_dai_sysclk
@@ -1560,39 +1575,48 @@ static int wm8903_resume(struct platform_device *pdev)
        return 0;
 }
 
-/*
- * initialise the WM8903 driver
- * register the mixer and dsp interfaces with the kernel
- */
-static int wm8903_init(struct snd_soc_device *socdev)
+static struct snd_soc_codec *wm8903_codec;
+
+static int wm8903_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
 {
-       struct snd_soc_codec *codec = socdev->codec;
-       struct i2c_client *i2c = codec->control_data;
-       int ret = 0;
+       struct wm8903_priv *wm8903;
+       struct snd_soc_codec *codec;
+       int ret;
        u16 val;
 
-       val = wm8903_hw_read(codec, WM8903_SW_RESET_AND_ID);
-       if (val != wm8903_reg_defaults[WM8903_SW_RESET_AND_ID]) {
-               dev_err(&i2c->dev,
-                       "Device with ID register %x is not a WM8903\n", val);
-               return -ENODEV;
-       }
+       wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL);
+       if (wm8903 == NULL)
+               return -ENOMEM;
+
+       codec = &wm8903->codec;
 
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->dev = &i2c->dev;
        codec->name = "WM8903";
        codec->owner = THIS_MODULE;
        codec->read = wm8903_read;
        codec->write = wm8903_write;
+       codec->hw_write = (hw_write_t)i2c_master_send;
        codec->bias_level = SND_SOC_BIAS_OFF;
        codec->set_bias_level = wm8903_set_bias_level;
        codec->dai = &wm8903_dai;
        codec->num_dai = 1;
-       codec->reg_cache_size = ARRAY_SIZE(wm8903_reg_defaults);
-       codec->reg_cache = kmemdup(wm8903_reg_defaults,
-                                  sizeof(wm8903_reg_defaults),
-                                  GFP_KERNEL);
-       if (codec->reg_cache == NULL) {
-               dev_err(&i2c->dev, "Failed to allocate register cache\n");
-               return -ENOMEM;
+       codec->reg_cache_size = ARRAY_SIZE(wm8903->reg_cache);
+       codec->reg_cache = &wm8903->reg_cache[0];
+       codec->private_data = wm8903;
+
+       i2c_set_clientdata(i2c, codec);
+       codec->control_data = i2c;
+
+       val = wm8903_hw_read(codec, WM8903_SW_RESET_AND_ID);
+       if (val != wm8903_reg_defaults[WM8903_SW_RESET_AND_ID]) {
+               dev_err(&i2c->dev,
+                       "Device with ID register %x is not a WM8903\n", val);
+               return -ENODEV;
        }
 
        val = wm8903_read(codec, WM8903_REVISION_NUMBER);
@@ -1601,16 +1625,6 @@ static int wm8903_init(struct snd_soc_device *socdev)
 
        wm8903_reset(codec);
 
-       /* register pcms */
-       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-       if (ret < 0) {
-               dev_err(&i2c->dev, "failed to create pcms\n");
-               goto pcm_err;
-       }
-
-       /* SYSCLK is required for pretty much anything */
-       wm8903_write(codec, WM8903_CLOCK_RATES_2, WM8903_CLK_SYS_ENA);
-
        /* power on device */
        wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
@@ -1645,47 +1659,45 @@ static int wm8903_init(struct snd_soc_device *socdev)
        val |= WM8903_DAC_MUTEMODE;
        wm8903_write(codec, WM8903_DAC_DIGITAL_1, val);
 
-       wm8903_add_controls(codec);
-       wm8903_add_widgets(codec);
-       ret = snd_soc_register_card(socdev);
-       if (ret < 0) {
-               dev_err(&i2c->dev, "wm8903: failed to register card\n");
-               goto card_err;
+       wm8903_dai.dev = &i2c->dev;
+       wm8903_codec = codec;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+               goto err;
+       }
+
+       ret = snd_soc_register_dai(&wm8903_dai);
+       if (ret != 0) {
+               dev_err(&i2c->dev, "Failed to register DAI: %d\n", ret);
+               goto err_codec;
        }
 
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
-pcm_err:
-       kfree(codec->reg_cache);
+err_codec:
+       snd_soc_unregister_codec(codec);
+err:
+       wm8903_codec = NULL;
+       kfree(wm8903);
        return ret;
 }
 
-static struct snd_soc_device *wm8903_socdev;
-
-static int wm8903_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int wm8903_i2c_remove(struct i2c_client *client)
 {
-       struct snd_soc_device *socdev = wm8903_socdev;
-       struct snd_soc_codec *codec = socdev->codec;
-       int ret;
+       struct snd_soc_codec *codec = i2c_get_clientdata(client);
 
-       i2c_set_clientdata(i2c, codec);
-       codec->control_data = i2c;
+       snd_soc_unregister_dai(&wm8903_dai);
+       snd_soc_unregister_codec(codec);
 
-       ret = wm8903_init(socdev);
-       if (ret < 0)
-               dev_err(&i2c->dev, "Device initialisation failed\n");
+       wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
-       return ret;
-}
+       kfree(codec->private_data);
+
+       wm8903_codec = NULL;
+       wm8903_dai.dev = NULL;
 
-static int wm8903_i2c_remove(struct i2c_client *client)
-{
-       struct snd_soc_codec *codec = i2c_get_clientdata(client);
-       kfree(codec->reg_cache);
        return 0;
 }
 
@@ -1709,75 +1721,37 @@ static struct i2c_driver wm8903_i2c_driver = {
 static int wm8903_probe(struct platform_device *pdev)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct wm8903_setup_data *setup;
-       struct snd_soc_codec *codec;
-       struct wm8903_priv *wm8903;
-       struct i2c_board_info board_info;
-       struct i2c_adapter *adapter;
-       struct i2c_client *i2c_client;
        int ret = 0;
 
-       setup = socdev->codec_data;
-
-       if (!setup->i2c_address) {
-               dev_err(&pdev->dev, "No codec address provided\n");
-               return -ENODEV;
+       if (!wm8903_codec) {
+               dev_err(&pdev->dev, "I2C device not yet probed\n");
+               goto err;
        }
 
-       codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-       if (codec == NULL)
-               return -ENOMEM;
+       socdev->codec = wm8903_codec;
 
-       wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL);
-       if (wm8903 == NULL) {
-               ret = -ENOMEM;
-               goto err_codec;
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to create pcms\n");
+               goto err;
        }
 
-       codec->private_data = wm8903;
-       socdev->codec = codec;
-       mutex_init(&codec->mutex);
-       INIT_LIST_HEAD(&codec->dapm_widgets);
-       INIT_LIST_HEAD(&codec->dapm_paths);
-
-       wm8903_socdev = socdev;
+       wm8903_add_controls(socdev->codec);
+       wm8903_add_widgets(socdev->codec);
 
-       codec->hw_write = (hw_write_t)i2c_master_send;
-       ret = i2c_add_driver(&wm8903_i2c_driver);
-       if (ret != 0) {
-               dev_err(&pdev->dev, "can't add i2c driver\n");
-               goto err_priv;
-       } else {
-               memset(&board_info, 0, sizeof(board_info));
-               strlcpy(board_info.type, "wm8903", I2C_NAME_SIZE);
-               board_info.addr = setup->i2c_address;
-
-               adapter = i2c_get_adapter(setup->i2c_bus);
-               if (!adapter) {
-                       dev_err(&pdev->dev, "Can't get I2C bus %d\n",
-                               setup->i2c_bus);
-                       ret = -ENODEV;
-                       goto err_adapter;
-               }
-
-               i2c_client = i2c_new_device(adapter, &board_info);
-               i2c_put_adapter(adapter);
-               if (i2c_client == NULL) {
-                       dev_err(&pdev->dev,
-                               "I2C driver registration failed\n");
-                       ret = -ENODEV;
-                       goto err_adapter;
-               }
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "wm8903: failed to register card\n");
+               goto card_err;
        }
 
        return ret;
 
-err_adapter:
-       i2c_del_driver(&wm8903_i2c_driver);
-err_priv:
-       kfree(codec->private_data);
-err_codec:
-       kfree(codec);
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+err:
        return ret;
 }
 
@@ -1792,10 +1766,6 @@ static int wm8903_remove(struct platform_device *pdev)
 
        snd_soc_free_pcms(socdev);
        snd_soc_dapm_free(socdev);
-       i2c_unregister_device(socdev->codec->control_data);
-       i2c_del_driver(&wm8903_i2c_driver);
-       kfree(codec->private_data);
-       kfree(codec);
 
        return 0;
 }
@@ -1808,6 +1778,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8903 = {
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8903);
 
+static int __init wm8903_modinit(void)
+{
+       return i2c_add_driver(&wm8903_i2c_driver);
+}
+module_init(wm8903_modinit);
+
+static void __exit wm8903_exit(void)
+{
+       i2c_del_driver(&wm8903_i2c_driver);
+}
+module_exit(wm8903_exit);
+
 MODULE_DESCRIPTION("ASoC WM8903 driver");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.cm>");
 MODULE_LICENSE("GPL");
index cec622f..0ea27e2 100644 (file)
 extern struct snd_soc_dai wm8903_dai;
 extern struct snd_soc_codec_device soc_codec_dev_wm8903;
 
-struct wm8903_setup_data {
-       int i2c_bus;
-       int i2c_address;
-};
-
 #define WM8903_MCLK_DIV_2 1
 #define WM8903_CLK_SYS    2
 #define WM8903_BCLK       3
index f41a578..88ead7f 100644 (file)
@@ -541,7 +541,8 @@ static int wm8971_set_dai_fmt(struct snd_soc_dai *codec_dai,
 }
 
 static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
+       struct snd_pcm_hw_params *params,
+       struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -634,8 +635,6 @@ struct snd_soc_dai wm8971_dai = {
                .formats = WM8971_FORMATS,},
        .ops = {
                .hw_params = wm8971_pcm_hw_params,
-       },
-       .dai_ops = {
                .digital_mute = wm8971_mute,
                .set_fmt = wm8971_set_dai_fmt,
                .set_sysclk = wm8971_set_dai_sysclk,
@@ -748,7 +747,7 @@ static int wm8971_init(struct snd_soc_device *socdev)
 
        wm8971_add_controls(codec);
        wm8971_add_widgets(codec);
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                printk(KERN_ERR "wm8971: failed to register card\n");
                goto card_err;
@@ -936,6 +935,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8971 = {
 
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8971);
 
+static int __init wm8971_modinit(void)
+{
+       return snd_soc_register_dai(&wm8971_dai);
+}
+module_init(wm8971_modinit);
+
+static void __exit wm8971_exit(void)
+{
+       snd_soc_unregister_dai(&wm8971_dai);
+}
+module_exit(wm8971_exit);
+
 MODULE_DESCRIPTION("ASoC WM8971 driver");
 MODULE_AUTHOR("Lab126");
 MODULE_LICENSE("GPL");
index 572d22b..5b5afc1 100644 (file)
@@ -106,6 +106,7 @@ static const u16 wm8990_reg[] = {
        0x0008,     /* R60 - PLL1 */
        0x0031,     /* R61 - PLL2 */
        0x0026,     /* R62 - PLL3 */
+       0x0000,     /* R63 - Driver internal */
 };
 
 /*
@@ -126,10 +127,9 @@ static inline void wm8990_write_reg_cache(struct snd_soc_codec *codec,
        unsigned int reg, unsigned int value)
 {
        u16 *cache = codec->reg_cache;
-       BUG_ON(reg > (ARRAY_SIZE(wm8990_reg)) - 1);
 
-       /* Reset register is uncached */
-       if (reg == 0)
+       /* Reset register and reserved registers are uncached */
+       if (reg == 0 || reg > ARRAY_SIZE(wm8990_reg) - 1)
                return;
 
        cache[reg] = value;
@@ -1172,7 +1172,8 @@ static int wm8990_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
  * Set PCM DAI bit size and sample rate.
  */
 static int wm8990_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
@@ -1222,8 +1223,14 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
        switch (level) {
        case SND_SOC_BIAS_ON:
                break;
+
        case SND_SOC_BIAS_PREPARE:
+               /* VMID=2*50k */
+               val = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_1) &
+                       ~WM8990_VMID_MODE_MASK;
+               wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, val | 0x2);
                break;
+
        case SND_SOC_BIAS_STANDBY:
                if (codec->bias_level == SND_SOC_BIAS_OFF) {
                        /* Enable all output discharge bits */
@@ -1272,10 +1279,17 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
 
                        /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
                        wm8990_write(codec, WM8990_ANTIPOP2, WM8990_BUFIOEN);
-               } else {
-                       /* ON -> standby */
 
+                       /* Enable workaround for ADC clocking issue. */
+                       wm8990_write(codec, WM8990_EXT_ACCESS_ENA, 0x2);
+                       wm8990_write(codec, WM8990_EXT_CTL1, 0xa003);
+                       wm8990_write(codec, WM8990_EXT_ACCESS_ENA, 0);
                }
+
+               /* VMID=2*250k */
+               val = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_1) &
+                       ~WM8990_VMID_MODE_MASK;
+               wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, val | 0x4);
                break;
 
        case SND_SOC_BIAS_OFF:
@@ -1349,8 +1363,7 @@ struct snd_soc_dai wm8990_dai = {
                .rates = WM8990_RATES,
                .formats = WM8990_FORMATS,},
        .ops = {
-               .hw_params = wm8990_hw_params,},
-       .dai_ops = {
+               .hw_params = wm8990_hw_params,
                .digital_mute = wm8990_mute,
                .set_fmt = wm8990_set_dai_fmt,
                .set_clkdiv = wm8990_set_dai_clkdiv,
@@ -1449,7 +1462,7 @@ static int wm8990_init(struct snd_soc_device *socdev)
 
        wm8990_add_controls(codec);
        wm8990_add_widgets(codec);
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                printk(KERN_ERR "wm8990: failed to register card\n");
                goto card_err;
@@ -1630,6 +1643,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8990 = {
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8990);
 
+static int __init wm8990_modinit(void)
+{
+       return snd_soc_register_dai(&wm8990_dai);
+}
+module_init(wm8990_modinit);
+
+static void __exit wm8990_exit(void)
+{
+       snd_soc_unregister_dai(&wm8990_dai);
+}
+module_exit(wm8990_exit);
+
 MODULE_DESCRIPTION("ASoC WM8990 driver");
 MODULE_AUTHOR("Liam Girdwood");
 MODULE_LICENSE("GPL");
index 0e192f3..7114ddc 100644 (file)
@@ -80,8 +80,8 @@
 #define WM8990_PLL3                             0x3E
 #define WM8990_INTDRIVBITS                     0x3F
 
-#define WM8990_REGISTER_COUNT                   60
-#define WM8990_MAX_REGISTER                     0x3F
+#define WM8990_EXT_ACCESS_ENA                  0x75
+#define WM8990_EXT_CTL1                                0x7a
 
 /*
  * Field Definitions.
index ffb471e..af83d62 100644 (file)
@@ -487,7 +487,8 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
        return 0;
 }
 
-static int ac97_prepare(struct snd_pcm_substream *substream)
+static int ac97_prepare(struct snd_pcm_substream *substream,
+                       struct snd_soc_dai *dai)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -507,7 +508,8 @@ static int ac97_prepare(struct snd_pcm_substream *substream)
        return ac97_write(codec, reg, runtime->rate);
 }
 
-static int ac97_aux_prepare(struct snd_pcm_substream *substream)
+static int ac97_aux_prepare(struct snd_pcm_substream *substream,
+                           struct snd_soc_dai *dai)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -533,7 +535,7 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
 struct snd_soc_dai wm9712_dai[] = {
 {
        .name = "AC97 HiFi",
-       .type = SND_SOC_DAI_AC97_BUS,
+       .ac97_control = 1,
        .playback = {
                .stream_name = "HiFi Playback",
                .channels_min = 1,
@@ -688,7 +690,7 @@ static int wm9712_soc_probe(struct platform_device *pdev)
 
        ret = wm9712_reset(codec, 0);
        if (ret < 0) {
-               printk(KERN_ERR "AC97 link error\n");
+               printk(KERN_ERR "Failed to reset WM9712: AC97 link error\n");
                goto reset_err;
        }
 
@@ -698,7 +700,7 @@ static int wm9712_soc_probe(struct platform_device *pdev)
        wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
        wm9712_add_controls(codec);
        wm9712_add_widgets(codec);
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                printk(KERN_ERR "wm9712: failed to register card\n");
                goto reset_err;
index 945b32e..f3ca8aa 100644 (file)
@@ -928,11 +928,10 @@ static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai,
 }
 
 static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
 {
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_device *socdev = rtd->socdev;
-       struct snd_soc_codec *codec = socdev->codec;
+       struct snd_soc_codec *codec = dai->codec;
        u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3;
 
        switch (params_format(params)) {
@@ -954,11 +953,10 @@ static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static void wm9713_voiceshutdown(struct snd_pcm_substream *substream)
+static void wm9713_voiceshutdown(struct snd_pcm_substream *substream,
+                                struct snd_soc_dai *dai)
 {
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_device *socdev = rtd->socdev;
-       struct snd_soc_codec *codec = socdev->codec;
+       struct snd_soc_codec *codec = dai->codec;
        u16 status;
 
        /* Gracefully shut down the voice interface. */
@@ -969,12 +967,11 @@ static void wm9713_voiceshutdown(struct snd_pcm_substream *substream)
        ac97_write(codec, AC97_EXTENDED_MID, status);
 }
 
-static int ac97_hifi_prepare(struct snd_pcm_substream *substream)
+static int ac97_hifi_prepare(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
 {
+       struct snd_soc_codec *codec = dai->codec;
        struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_device *socdev = rtd->socdev;
-       struct snd_soc_codec *codec = socdev->codec;
        int reg;
        u16 vra;
 
@@ -989,12 +986,11 @@ static int ac97_hifi_prepare(struct snd_pcm_substream *substream)
        return ac97_write(codec, reg, runtime->rate);
 }
 
-static int ac97_aux_prepare(struct snd_pcm_substream *substream)
+static int ac97_aux_prepare(struct snd_pcm_substream *substream,
+                           struct snd_soc_dai *dai)
 {
+       struct snd_soc_codec *codec = dai->codec;
        struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_device *socdev = rtd->socdev;
-       struct snd_soc_codec *codec = socdev->codec;
        u16 vra, xsle;
 
        vra = ac97_read(codec, AC97_EXTENDED_STATUS);
@@ -1028,7 +1024,7 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
 struct snd_soc_dai wm9713_dai[] = {
 {
        .name = "AC97 HiFi",
-       .type = SND_SOC_DAI_AC97_BUS,
+       .ac97_control = 1,
        .playback = {
                .stream_name = "HiFi Playback",
                .channels_min = 1,
@@ -1042,8 +1038,7 @@ struct snd_soc_dai wm9713_dai[] = {
                .rates = WM9713_RATES,
                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
        .ops = {
-               .prepare = ac97_hifi_prepare,},
-       .dai_ops = {
+               .prepare = ac97_hifi_prepare,
                .set_clkdiv = wm9713_set_dai_clkdiv,
                .set_pll = wm9713_set_dai_pll,},
        },
@@ -1056,8 +1051,7 @@ struct snd_soc_dai wm9713_dai[] = {
                .rates = WM9713_RATES,
                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
        .ops = {
-               .prepare = ac97_aux_prepare,},
-       .dai_ops = {
+               .prepare = ac97_aux_prepare,
                .set_clkdiv = wm9713_set_dai_clkdiv,
                .set_pll = wm9713_set_dai_pll,},
        },
@@ -1077,8 +1071,7 @@ struct snd_soc_dai wm9713_dai[] = {
                .formats = WM9713_PCM_FORMATS,},
        .ops = {
                .hw_params = wm9713_pcm_hw_params,
-               .shutdown = wm9713_voiceshutdown,},
-       .dai_ops = {
+               .shutdown = wm9713_voiceshutdown,
                .set_clkdiv = wm9713_set_dai_clkdiv,
                .set_pll = wm9713_set_dai_pll,
                .set_fmt = wm9713_set_dai_fmt,
@@ -1097,6 +1090,8 @@ int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
        }
 
        soc_ac97_ops.reset(codec->ac97);
+       if (soc_ac97_ops.warm_reset)
+               soc_ac97_ops.warm_reset(codec->ac97);
        if (ac97_read(codec, 0) != wm9713_reg[0])
                return -EIO;
        return 0;
@@ -1240,7 +1235,7 @@ static int wm9713_soc_probe(struct platform_device *pdev)
        wm9713_reset(codec, 0);
        ret = wm9713_reset(codec, 1);
        if (ret < 0) {
-               printk(KERN_ERR "AC97 link error\n");
+               printk(KERN_ERR "Failed to reset WM9713: AC97 link error\n");
                goto reset_err;
        }
 
@@ -1252,7 +1247,7 @@ static int wm9713_soc_probe(struct platform_device *pdev)
 
        wm9713_add_controls(codec);
        wm9713_add_widgets(codec);
-       ret = snd_soc_register_card(socdev);
+       ret = snd_soc_init_card(socdev);
        if (ret < 0)
                goto reset_err;
        return 0;
@@ -1288,7 +1283,6 @@ static int wm9713_soc_remove(struct platform_device *pdev)
        snd_soc_free_ac97_codec(codec);
        kfree(codec->private_data);
        kfree(codec->reg_cache);
-       kfree(codec->dai);
        kfree(codec);
        return 0;
 }
index 8f7e338..b502741 100644 (file)
@@ -17,3 +17,13 @@ config SND_DAVINCI_SOC_EVM
        help
          Say Y if you want to add support for SoC audio on TI
          DaVinci EVM platform.
+
+config SND_DAVINCI_SOC_SFFSDR
+       tristate "SoC Audio support for SFFSDR"
+       depends on SND_DAVINCI_SOC && MACH_DAVINCI_SFFSDR
+       select SND_DAVINCI_SOC_I2S
+       select SND_SOC_PCM3008
+       select SFFSDR_FPGA
+       help
+         Say Y if you want to add support for SoC audio on
+         Lyrtech SFFSDR board.
index ca772e5..ca8bae1 100644 (file)
@@ -7,5 +7,7 @@ obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o
 
 # DAVINCI Machine Support
 snd-soc-evm-objs := davinci-evm.o
+snd-soc-sffsdr-objs := davinci-sffsdr.o
 
 obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o
+obj-$(CONFIG_SND_DAVINCI_SOC_SFFSDR) += snd-soc-sffsdr.o
index 9e6062c..01b948b 100644 (file)
@@ -28,6 +28,8 @@
 
 #define EVM_CODEC_CLOCK 22579200
 
+#define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \
+               SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF)
 static int evm_hw_params(struct snd_pcm_substream *substream,
                         struct snd_pcm_hw_params *params)
 {
@@ -37,14 +39,12 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
        int ret = 0;
 
        /* set codec DAI configuration */
-       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
-                                        SND_SOC_DAIFMT_CBM_CFM);
+       ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT);
        if (ret < 0)
                return ret;
 
        /* set cpu DAI configuration */
-       ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM |
-                                      SND_SOC_DAIFMT_IB_NF);
+       ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT);
        if (ret < 0)
                return ret;
 
@@ -128,8 +128,9 @@ static struct snd_soc_dai_link evm_dai = {
 };
 
 /* davinci-evm audio machine driver */
-static struct snd_soc_machine snd_soc_machine_evm = {
+static struct snd_soc_card snd_soc_card_evm = {
        .name = "DaVinci EVM",
+       .platform = &davinci_soc_platform,
        .dai_link = &evm_dai,
        .num_links = 1,
 };
@@ -142,8 +143,7 @@ static struct aic3x_setup_data evm_aic3x_setup = {
 
 /* evm audio subsystem */
 static struct snd_soc_device evm_snd_devdata = {
-       .machine = &snd_soc_machine_evm,
-       .platform = &davinci_soc_platform,
+       .card = &snd_soc_card_evm,
        .codec_dev = &soc_codec_dev_aic3x,
        .codec_data = &evm_aic3x_setup,
 };
index abb5fed..0fee779 100644 (file)
@@ -59,6 +59,7 @@
 #define DAVINCI_MCBSP_PCR_CLKXP                (1 << 1)
 #define DAVINCI_MCBSP_PCR_FSRP         (1 << 2)
 #define DAVINCI_MCBSP_PCR_FSXP         (1 << 3)
+#define DAVINCI_MCBSP_PCR_SCLKME       (1 << 7)
 #define DAVINCI_MCBSP_PCR_CLKRM                (1 << 8)
 #define DAVINCI_MCBSP_PCR_CLKXM                (1 << 9)
 #define DAVINCI_MCBSP_PCR_FSRM         (1 << 10)
@@ -110,16 +111,59 @@ static void davinci_mcbsp_start(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_platform *platform = socdev->card->platform;
        u32 w;
+       int ret;
 
        /* Start the sample generator and enable transmitter/receiver */
        w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
        MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST, 1);
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               /* Stop the DMA to avoid data loss */
+               /* while the transmitter is out of reset to handle XSYNCERR */
+               if (platform->pcm_ops->trigger) {
+                       ret = platform->pcm_ops->trigger(substream,
+                               SNDRV_PCM_TRIGGER_STOP);
+                       if (ret < 0)
+                               printk(KERN_DEBUG "Playback DMA stop failed\n");
+               }
+
+               /* Enable the transmitter */
+               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
                MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
-       else
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+               /* wait for any unexpected frame sync error to occur */
+               udelay(100);
+
+               /* Disable the transmitter to clear any outstanding XSYNCERR */
+               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+               MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0);
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+               /* Restart the DMA */
+               if (platform->pcm_ops->trigger) {
+                       ret = platform->pcm_ops->trigger(substream,
+                               SNDRV_PCM_TRIGGER_START);
+                       if (ret < 0)
+                               printk(KERN_DEBUG "Playback DMA start failed\n");
+               }
+               /* Enable the transmitter */
+               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+               MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+       } else {
+
+               /* Enable the reciever */
+               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
                MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 1);
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+       }
+
 
        /* Start frame sync */
        w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
@@ -144,7 +188,8 @@ static void davinci_mcbsp_stop(struct snd_pcm_substream *substream)
        davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
 }
 
-static int davinci_i2s_startup(struct snd_pcm_substream *substream)
+static int davinci_i2s_startup(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -155,61 +200,138 @@ static int davinci_i2s_startup(struct snd_pcm_substream *substream)
        return 0;
 }
 
+#define DEFAULT_BITPERSAMPLE   16
+
 static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                                   unsigned int fmt)
 {
        struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
-       u32 w;
+       unsigned int pcr;
+       unsigned int srgr;
+       unsigned int rcr;
+       unsigned int xcr;
+       srgr = DAVINCI_MCBSP_SRGR_FSGM |
+               DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
+               DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
 
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
        case SND_SOC_DAIFMT_CBS_CFS:
-               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG,
-                                       DAVINCI_MCBSP_PCR_FSXM |
-                                       DAVINCI_MCBSP_PCR_FSRM |
-                                       DAVINCI_MCBSP_PCR_CLKXM |
-                                       DAVINCI_MCBSP_PCR_CLKRM);
-               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG,
-                                       DAVINCI_MCBSP_SRGR_FSGM);
+               /* cpu is master */
+               pcr = DAVINCI_MCBSP_PCR_FSXM |
+                       DAVINCI_MCBSP_PCR_FSRM |
+                       DAVINCI_MCBSP_PCR_CLKXM |
+                       DAVINCI_MCBSP_PCR_CLKRM;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+               /* McBSP CLKR pin is the input for the Sample Rate Generator.
+                * McBSP FSR and FSX are driven by the Sample Rate Generator. */
+               pcr = DAVINCI_MCBSP_PCR_SCLKME |
+                       DAVINCI_MCBSP_PCR_FSXM |
+                       DAVINCI_MCBSP_PCR_FSRM;
                break;
        case SND_SOC_DAIFMT_CBM_CFM:
-               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, 0);
+               /* codec is master */
+               pcr = 0;
                break;
        default:
+               printk(KERN_ERR "%s:bad master\n", __func__);
                return -EINVAL;
        }
 
-       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-       case SND_SOC_DAIFMT_IB_NF:
-               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG);
-               MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_CLKXP |
-                              DAVINCI_MCBSP_PCR_CLKRP, 1);
-               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w);
+       rcr = DAVINCI_MCBSP_RCR_RFRLEN1(1);
+       xcr = DAVINCI_MCBSP_XCR_XFIG | DAVINCI_MCBSP_XCR_XFRLEN1(1);
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_B:
                break;
-       case SND_SOC_DAIFMT_NB_IF:
-               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG);
-               MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_FSXP |
-                              DAVINCI_MCBSP_PCR_FSRP, 1);
-               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w);
+       case SND_SOC_DAIFMT_I2S:
+               /* Davinci doesn't support TRUE I2S, but some codecs will have
+                * the left and right channels contiguous. This allows
+                * dsp_a mode to be used with an inverted normal frame clk.
+                * If your codec is master and does not have contiguous
+                * channels, then you will have sound on only one channel.
+                * Try using a different mode, or codec as slave.
+                *
+                * The TLV320AIC33 is an example of a codec where this works.
+                * It has a variable bit clock frequency allowing it to have
+                * valid data on every bit clock.
+                *
+                * The TLV320AIC23 is an example of a codec where this does not
+                * work. It has a fixed bit clock frequency with progressively
+                * more empty bit clock slots between channels as the sample
+                * rate is lowered.
+                */
+               fmt ^= SND_SOC_DAIFMT_NB_IF;
+       case SND_SOC_DAIFMT_DSP_A:
+               rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
+               xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
+               break;
+       default:
+               printk(KERN_ERR "%s:bad format\n", __func__);
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               /* CLKRP Receive clock polarity,
+                *      1 - sampled on rising edge of CLKR
+                *      valid on rising edge
+                * CLKXP Transmit clock polarity,
+                *      1 - clocked on falling edge of CLKX
+                *      valid on rising edge
+                * FSRP  Receive frame sync pol, 0 - active high
+                * FSXP  Transmit frame sync pol, 0 - active high
+                */
+               pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP);
                break;
        case SND_SOC_DAIFMT_IB_IF:
-               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG);
-               MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_CLKXP |
-                              DAVINCI_MCBSP_PCR_CLKRP |
-                              DAVINCI_MCBSP_PCR_FSXP |
-                              DAVINCI_MCBSP_PCR_FSRP, 1);
-               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w);
+               /* CLKRP Receive clock polarity,
+                *      0 - sampled on falling edge of CLKR
+                *      valid on falling edge
+                * CLKXP Transmit clock polarity,
+                *      0 - clocked on rising edge of CLKX
+                *      valid on falling edge
+                * FSRP  Receive frame sync pol, 1 - active low
+                * FSXP  Transmit frame sync pol, 1 - active low
+                */
+               pcr |= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
                break;
-       case SND_SOC_DAIFMT_NB_NF:
+       case SND_SOC_DAIFMT_NB_IF:
+               /* CLKRP Receive clock polarity,
+                *      1 - sampled on rising edge of CLKR
+                *      valid on rising edge
+                * CLKXP Transmit clock polarity,
+                *      1 - clocked on falling edge of CLKX
+                *      valid on rising edge
+                * FSRP  Receive frame sync pol, 1 - active low
+                * FSXP  Transmit frame sync pol, 1 - active low
+                */
+               pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP |
+                       DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               /* CLKRP Receive clock polarity,
+                *      0 - sampled on falling edge of CLKR
+                *      valid on falling edge
+                * CLKXP Transmit clock polarity,
+                *      0 - clocked on rising edge of CLKX
+                *      valid on falling edge
+                * FSRP  Receive frame sync pol, 0 - active high
+                * FSXP  Transmit frame sync pol, 0 - active high
+                */
                break;
        default:
                return -EINVAL;
        }
-
+       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
+       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, pcr);
+       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
+       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
        return 0;
 }
 
 static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
-                                struct snd_pcm_hw_params *params)
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct davinci_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data;
@@ -219,25 +341,20 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
        u32 w;
 
        /* general line settings */
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG,
-                               DAVINCI_MCBSP_SPCR_RINTM(3) |
-                               DAVINCI_MCBSP_SPCR_XINTM(3) |
-                               DAVINCI_MCBSP_SPCR_FREE);
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG,
-                               DAVINCI_MCBSP_RCR_RFRLEN1(1) |
-                               DAVINCI_MCBSP_RCR_RDATDLY(1));
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG,
-                               DAVINCI_MCBSP_XCR_XFRLEN1(1) |
-                               DAVINCI_MCBSP_XCR_XDATDLY(1) |
-                               DAVINCI_MCBSP_XCR_XFIG);
+       w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               w |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+       } else {
+               w |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+       }
 
        i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
-       w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG);
+       w = DAVINCI_MCBSP_SRGR_FSGM;
        MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1), 1);
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w);
 
        i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
-       w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG);
        MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1), 1);
        davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w);
 
@@ -260,20 +377,24 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG);
-       MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
-                      DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length), 1);
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w);
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG);
+               MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
+                              DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length), 1);
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w);
 
-       w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG);
-       MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
-                      DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length), 1);
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w);
+       } else {
+               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG);
+               MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
+                              DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length), 1);
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w);
 
+       }
        return 0;
 }
 
-static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+                              struct snd_soc_dai *dai)
 {
        int ret = 0;
 
@@ -299,8 +420,8 @@ static int davinci_i2s_probe(struct platform_device *pdev,
                             struct snd_soc_dai *dai)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_machine *machine = socdev->machine;
-       struct snd_soc_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
+       struct snd_soc_card *card = socdev->card;
+       struct snd_soc_dai *cpu_dai = card->dai_link[pdev->id].cpu_dai;
        struct davinci_mcbsp_dev *dev;
        struct resource *mem, *ioarea;
        struct evm_snd_platform_data *pdata;
@@ -361,8 +482,8 @@ static void davinci_i2s_remove(struct platform_device *pdev,
                               struct snd_soc_dai *dai)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_machine *machine = socdev->machine;
-       struct snd_soc_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
+       struct snd_soc_card *card = socdev->card;
+       struct snd_soc_dai *cpu_dai = card->dai_link[pdev->id].cpu_dai;
        struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
        struct resource *mem;
 
@@ -381,7 +502,6 @@ static void davinci_i2s_remove(struct platform_device *pdev,
 struct snd_soc_dai davinci_i2s_dai = {
        .name = "davinci-i2s",
        .id = 0,
-       .type = SND_SOC_DAI_I2S,
        .probe = davinci_i2s_probe,
        .remove = davinci_i2s_remove,
        .playback = {
@@ -397,13 +517,24 @@ struct snd_soc_dai davinci_i2s_dai = {
        .ops = {
                .startup = davinci_i2s_startup,
                .trigger = davinci_i2s_trigger,
-               .hw_params = davinci_i2s_hw_params,},
-       .dai_ops = {
+               .hw_params = davinci_i2s_hw_params,
                .set_fmt = davinci_i2s_set_dai_fmt,
        },
 };
 EXPORT_SYMBOL_GPL(davinci_i2s_dai);
 
+static int __init davinci_i2s_init(void)
+{
+       return snd_soc_register_dai(&davinci_i2s_dai);
+}
+module_init(davinci_i2s_init);
+
+static void __exit davinci_i2s_exit(void)
+{
+       snd_soc_unregister_dai(&davinci_i2s_dai);
+}
+module_exit(davinci_i2s_exit);
+
 MODULE_AUTHOR("Vladimir Barinov");
 MODULE_DESCRIPTION("TI DAVINCI I2S (McBSP) SoC Interface");
 MODULE_LICENSE("GPL");
index 76feaa6..74abc9b 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/dma-mapping.h>
+#include <linux/kernel.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
 
 #include "davinci-pcm.h"
 
-#define DAVINCI_PCM_DEBUG 0
-#if DAVINCI_PCM_DEBUG
-#define DPRINTK(x...) printk(KERN_DEBUG x)
-#else
-#define DPRINTK(x...)
-#endif
-
 static struct snd_pcm_hardware davinci_pcm_hardware = {
        .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
                 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
@@ -78,8 +72,8 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
        dma_offset = prtd->period * period_size;
        dma_pos = runtime->dma_addr + dma_offset;
 
-       DPRINTK("audio_set_dma_params_play channel = %d dma_ptr = %x "
-               "period_size=%x\n", lch, dma_pos, period_size);
+       pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d "
+               "dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size);
 
        data_type = prtd->params->data_type;
        count = period_size / data_type;
@@ -112,7 +106,7 @@ static void davinci_pcm_dma_irq(int lch, u16 ch_status, void *data)
        struct snd_pcm_substream *substream = data;
        struct davinci_runtime_data *prtd = substream->runtime->private_data;
 
-       DPRINTK("lch=%d, status=0x%x\n", lch, ch_status);
+       pr_debug("davinci_pcm: lch=%d, status=0x%x\n", lch, ch_status);
 
        if (unlikely(ch_status != DMA_COMPLETE))
                return;
@@ -316,8 +310,8 @@ static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
        buf->area = dma_alloc_writecombine(pcm->card->dev, size,
                                           &buf->addr, GFP_KERNEL);
 
-       DPRINTK("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
-               (void *) buf->area, (void *) buf->addr, size);
+       pr_debug("davinci_pcm: preallocate_dma_buffer: area=%p, addr=%p, "
+               "size=%d\n", (void *) buf->area, (void *) buf->addr, size);
 
        if (!buf->area)
                return -ENOMEM;
@@ -384,6 +378,18 @@ struct snd_soc_platform davinci_soc_platform = {
 };
 EXPORT_SYMBOL_GPL(davinci_soc_platform);
 
+static int __init davinci_soc_platform_init(void)
+{
+       return snd_soc_register_platform(&davinci_soc_platform);
+}
+module_init(davinci_soc_platform_init);
+
+static void __exit davinci_soc_platform_exit(void)
+{
+       snd_soc_unregister_platform(&davinci_soc_platform);
+}
+module_exit(davinci_soc_platform_exit);
+
 MODULE_AUTHOR("Vladimir Barinov");
 MODULE_DESCRIPTION("TI DAVINCI PCM DMA module");
 MODULE_LICENSE("GPL");
diff --git a/sound/soc/davinci/davinci-sffsdr.c b/sound/soc/davinci/davinci-sffsdr.c
new file mode 100644 (file)
index 0000000..f67579d
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * ASoC driver for Lyrtech SFFSDR board.
+ *
+ * Author:     Hugo Villeneuve
+ * Copyright (C) 2008 Lyrtech inc
+ *
+ * Based on ASoC driver for TI DAVINCI EVM platform, original copyright follow:
+ * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/dma.h>
+#include <asm/plat-sffsdr/sffsdr-fpga.h>
+
+#include <mach/mcbsp.h>
+#include <mach/edma.h>
+
+#include "../codecs/pcm3008.h"
+#include "davinci-pcm.h"
+#include "davinci-i2s.h"
+
+static int sffsdr_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       int fs;
+       int ret = 0;
+
+       /* Set cpu DAI configuration:
+        * CLKX and CLKR are the inputs for the Sample Rate Generator.
+        * FSX and FSR are outputs, driven by the sample Rate Generator. */
+       ret = snd_soc_dai_set_fmt(cpu_dai,
+                                 SND_SOC_DAIFMT_RIGHT_J |
+                                 SND_SOC_DAIFMT_CBM_CFS |
+                                 SND_SOC_DAIFMT_IB_NF);
+       if (ret < 0)
+               return ret;
+
+       /* Fsref can be 32000, 44100 or 48000. */
+       fs = params_rate(params);
+
+       pr_debug("sffsdr_hw_params: rate = %d Hz\n", fs);
+
+       return sffsdr_fpga_set_codec_fs(fs);
+}
+
+static struct snd_soc_ops sffsdr_ops = {
+       .hw_params = sffsdr_hw_params,
+};
+
+/* davinci-sffsdr digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link sffsdr_dai = {
+       .name = "PCM3008", /* Codec name */
+       .stream_name = "PCM3008 HiFi",
+       .cpu_dai = &davinci_i2s_dai,
+       .codec_dai = &pcm3008_dai,
+       .ops = &sffsdr_ops,
+};
+
+/* davinci-sffsdr audio machine driver */
+static struct snd_soc_card snd_soc_sffsdr = {
+       .name = "DaVinci SFFSDR",
+       .platform = &davinci_soc_platform,
+       .dai_link = &sffsdr_dai,
+       .num_links = 1,
+};
+
+/* sffsdr audio private data */
+static struct pcm3008_setup_data sffsdr_pcm3008_setup = {
+       .dem0_pin = GPIO(45),
+       .dem1_pin = GPIO(46),
+       .pdad_pin = GPIO(47),
+       .pdda_pin = GPIO(38),
+};
+
+/* sffsdr audio subsystem */
+static struct snd_soc_device sffsdr_snd_devdata = {
+       .card = &snd_soc_sffsdr,
+       .codec_dev = &soc_codec_dev_pcm3008,
+       .codec_data = &sffsdr_pcm3008_setup,
+};
+
+static struct resource sffsdr_snd_resources[] = {
+       {
+               .start = DAVINCI_MCBSP_BASE,
+               .end = DAVINCI_MCBSP_BASE + SZ_8K - 1,
+               .flags = IORESOURCE_MEM,
+       },
+};
+
+static struct evm_snd_platform_data sffsdr_snd_data = {
+       .tx_dma_ch      = DAVINCI_DMA_MCBSP_TX,
+       .rx_dma_ch      = DAVINCI_DMA_MCBSP_RX,
+};
+
+static struct platform_device *sffsdr_snd_device;
+
+static int __init sffsdr_init(void)
+{
+       int ret;
+
+       sffsdr_snd_device = platform_device_alloc("soc-audio", 0);
+       if (!sffsdr_snd_device) {
+               printk(KERN_ERR "platform device allocation failed\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(sffsdr_snd_device, &sffsdr_snd_devdata);
+       sffsdr_snd_devdata.dev = &sffsdr_snd_device->dev;
+       sffsdr_snd_device->dev.platform_data = &sffsdr_snd_data;
+
+       ret = platform_device_add_resources(sffsdr_snd_device,
+                                           sffsdr_snd_resources,
+                                           ARRAY_SIZE(sffsdr_snd_resources));
+       if (ret) {
+               printk(KERN_ERR "platform device add ressources failed\n");
+               goto error;
+       }
+
+       ret = platform_device_add(sffsdr_snd_device);
+       if (ret)
+               goto error;
+
+       return ret;
+
+error:
+       platform_device_put(sffsdr_snd_device);
+       return ret;
+}
+
+static void __exit sffsdr_exit(void)
+{
+       platform_device_unregister(sffsdr_snd_device);
+}
+
+module_init(sffsdr_init);
+module_exit(sffsdr_exit);
+
+MODULE_AUTHOR("Hugo Villeneuve");
+MODULE_DESCRIPTION("Lyrtech SFFSDR ASoC driver");
+MODULE_LICENSE("GPL");
index 8d73edc..95c12b2 100644 (file)
@@ -20,7 +20,7 @@ config SND_SOC_MPC8610_HPCD
 
 config SND_SOC_MPC5200_I2S
        tristate "Freescale MPC5200 PSC in I2S mode driver"
-       depends on SND_SOC && PPC_MPC52xx && PPC_BESTCOMM
+       depends on PPC_MPC52xx && PPC_BESTCOMM
        select SND_SOC_OF_SIMPLE
        select PPC_BESTCOMM_GEN_BD
        help
index d2d3da9..64993ed 100644 (file)
@@ -284,7 +284,7 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
  * fsl_dma_new: initialize this PCM driver.
  *
  * This function is called when the codec driver calls snd_soc_new_pcms(),
- * once for each .dai_link in the machine driver's snd_soc_machine
+ * once for each .dai_link in the machine driver's snd_soc_card
  * structure.
  */
 static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
@@ -853,6 +853,18 @@ int fsl_dma_configure(struct fsl_dma_info *dma_info)
 }
 EXPORT_SYMBOL_GPL(fsl_dma_configure);
 
+static int __init fsl_soc_platform_init(void)
+{
+       return snd_soc_register_platform(&fsl_soc_platform);
+}
+module_init(fsl_soc_platform_init);
+
+static void __exit fsl_soc_platform_exit(void)
+{
+       snd_soc_unregister_platform(&fsl_soc_platform);
+}
+module_exit(fsl_soc_platform_exit);
+
 MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
 MODULE_DESCRIPTION("Freescale Elo DMA ASoC PCM module");
 MODULE_LICENSE("GPL");
index 157a789..c6d6eb7 100644 (file)
@@ -266,7 +266,8 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
  * If this is the first stream open, then grab the IRQ and program most of
  * the SSI registers.
  */
-static int fsl_ssi_startup(struct snd_pcm_substream *substream)
+static int fsl_ssi_startup(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
@@ -411,7 +412,8 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream)
  * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the
  * clock master.
  */
-static int fsl_ssi_prepare(struct snd_pcm_substream *substream)
+static int fsl_ssi_prepare(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -441,7 +443,8 @@ static int fsl_ssi_prepare(struct snd_pcm_substream *substream)
  * The DMA channel is in external master start and pause mode, which
  * means the SSI completely controls the flow of data.
  */
-static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd)
+static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
+                          struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
@@ -490,7 +493,8 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd)
  *
  * Shutdown the SSI if there are no other substreams open.
  */
-static void fsl_ssi_shutdown(struct snd_pcm_substream *substream)
+static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
@@ -578,8 +582,6 @@ static struct snd_soc_dai fsl_ssi_dai_template = {
                .prepare = fsl_ssi_prepare,
                .shutdown = fsl_ssi_shutdown,
                .trigger = fsl_ssi_trigger,
-       },
-       .dai_ops = {
                .set_sysclk = fsl_ssi_set_sysclk,
                .set_fmt = fsl_ssi_set_fmt,
        },
@@ -671,6 +673,14 @@ struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
        fsl_ssi_dai->private_data = ssi_private;
        fsl_ssi_dai->name = ssi_private->name;
        fsl_ssi_dai->id = ssi_info->id;
+       fsl_ssi_dai->dev = ssi_info->dev;
+
+       ret = snd_soc_register_dai(fsl_ssi_dai);
+       if (ret != 0) {
+               dev_err(ssi_info->dev, "failed to register DAI: %d\n", ret);
+               kfree(fsl_ssi_dai);
+               return NULL;
+       }
 
        return fsl_ssi_dai;
 }
@@ -688,6 +698,8 @@ void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai)
 
        device_remove_file(ssi_private->dev, &ssi_private->dev_attr);
 
+       snd_soc_unregister_dai(&ssi_private->cpu_dai);
+
        kfree(ssi_private);
 }
 EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai);
index 94a02ea..9eb1ce1 100644 (file)
@@ -187,7 +187,8 @@ static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream)
  * If this is the first stream open, then grab the IRQ and program most of
  * the PSC registers.
  */
-static int psc_i2s_startup(struct snd_pcm_substream *substream)
+static int psc_i2s_startup(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
@@ -220,7 +221,8 @@ static int psc_i2s_startup(struct snd_pcm_substream *substream)
 }
 
 static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
-                                struct snd_pcm_hw_params *params)
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
@@ -256,7 +258,8 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int psc_i2s_hw_free(struct snd_pcm_substream *substream)
+static int psc_i2s_hw_free(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
 {
        snd_pcm_set_runtime_buffer(substream, NULL);
        return 0;
@@ -268,7 +271,8 @@ static int psc_i2s_hw_free(struct snd_pcm_substream *substream)
  * This function is called by ALSA to start, stop, pause, and resume the DMA
  * transfer of data.
  */
-static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+                          struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
@@ -383,7 +387,8 @@ static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
  *
  * Shutdown the PSC if there are no other substreams open.
  */
-static void psc_i2s_shutdown(struct snd_pcm_substream *substream)
+static void psc_i2s_shutdown(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
@@ -464,7 +469,6 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
  * psc_i2s_dai_template: template CPU Digital Audio Interface
  */
 static struct snd_soc_dai psc_i2s_dai_template = {
-       .type = SND_SOC_DAI_I2S,
        .playback = {
                .channels_min = 2,
                .channels_max = 2,
@@ -483,8 +487,6 @@ static struct snd_soc_dai psc_i2s_dai_template = {
                .hw_free = psc_i2s_hw_free,
                .shutdown = psc_i2s_shutdown,
                .trigger = psc_i2s_trigger,
-       },
-       .dai_ops = {
                .set_sysclk = psc_i2s_set_sysclk,
                .set_fmt = psc_i2s_set_fmt,
        },
@@ -826,6 +828,8 @@ static int __devinit psc_i2s_of_probe(struct of_device *op,
        if (rc)
                dev_info(psc_i2s->dev, "error creating sysfs files\n");
 
+       snd_soc_register_platform(&psc_i2s_pcm_soc_platform);
+
        /* Tell the ASoC OF helpers about it */
        of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node,
                                     &psc_i2s->dai);
@@ -839,6 +843,8 @@ static int __devexit psc_i2s_of_remove(struct of_device *op)
 
        dev_dbg(&op->dev, "psc_i2s_remove()\n");
 
+       snd_soc_unregister_platform(&psc_i2s_pcm_soc_platform);
+
        bcom_gen_bd_rx_release(psc_i2s->capture.bcom_task);
        bcom_gen_bd_tx_release(psc_i2s->playback.bcom_task);
 
index 94f89de..bcec3f6 100644 (file)
@@ -29,7 +29,7 @@
 struct mpc8610_hpcd_data {
        struct snd_soc_device sound_devdata;
        struct snd_soc_dai_link dai;
-       struct snd_soc_machine machine;
+       struct snd_soc_card machine;
        unsigned int dai_format;
        unsigned int codec_clk_direction;
        unsigned int cpu_clk_direction;
@@ -185,7 +185,7 @@ static struct snd_soc_ops mpc8610_hpcd_ops = {
 /**
  * mpc8610_hpcd_machine: ASoC machine data
  */
-static struct snd_soc_machine mpc8610_hpcd_machine = {
+static struct snd_soc_card mpc8610_hpcd_machine = {
        .probe = mpc8610_hpcd_machine_probe,
        .remove = mpc8610_hpcd_machine_remove,
        .name = "MPC8610 HPCD",
@@ -465,9 +465,9 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,
                goto error;
        }
 
-       machine_data->sound_devdata.machine = &mpc8610_hpcd_machine;
+       machine_data->sound_devdata.card = &mpc8610_hpcd_machine;
        machine_data->sound_devdata.codec_dev = &soc_codec_device_cs4270;
-       machine_data->sound_devdata.platform = &fsl_soc_platform;
+       machine_data->machine.platform = &fsl_soc_platform;
 
        sound_device->dev.platform_data = machine_data;
 
index 0382fda..8bc5cd9 100644 (file)
@@ -31,7 +31,7 @@ struct of_snd_soc_device {
        int id;
        struct list_head list;
        struct snd_soc_device device;
-       struct snd_soc_machine machine;
+       struct snd_soc_card card;
        struct snd_soc_dai_link dai_link;
        struct platform_device *pdev;
        struct device_node *platform_node;
@@ -58,9 +58,9 @@ of_snd_soc_get_device(struct device_node *codec_node)
        /* Initialize the structure and add it to the global list */
        of_soc->codec_node = codec_node;
        of_soc->id = of_snd_soc_next_index++;
-       of_soc->machine.dai_link = &of_soc->dai_link;
-       of_soc->machine.num_links = 1;
-       of_soc->device.machine = &of_soc->machine;
+       of_soc->card.dai_link = &of_soc->dai_link;
+       of_soc->card.num_links = 1;
+       of_soc->device.card = &of_soc->card;
        of_soc->dai_link.ops = &of_snd_soc_ops;
        list_add(&of_soc->list, &of_snd_soc_device_list);
 
@@ -158,8 +158,8 @@ int of_snd_soc_register_platform(struct snd_soc_platform *platform,
 
        of_soc->platform_node = node;
        of_soc->dai_link.cpu_dai = cpu_dai;
-       of_soc->device.platform = platform;
-       of_soc->machine.name = of_soc->dai_link.cpu_dai->name;
+       of_soc->card.platform = platform;
+       of_soc->card.name = of_soc->dai_link.cpu_dai->name;
 
        /* Now try to register the SoC device */
        of_snd_soc_register_device(of_soc);
index 8b7766b..a7b1d77 100644 (file)
@@ -1,6 +1,6 @@
 config SND_OMAP_SOC
        tristate "SoC Audio for the Texas Instruments OMAP chips"
-       depends on ARCH_OMAP && SND_SOC
+       depends on ARCH_OMAP
 
 config SND_OMAP_SOC_MCBSP
        tristate
@@ -21,3 +21,36 @@ config SND_OMAP_SOC_OSK5912
        select SND_SOC_TLV320AIC23
        help
          Say Y if you want to add support for SoC audio on osk5912.
+
+config SND_OMAP_SOC_OVERO
+       tristate "SoC Audio support for Gumstix Overo"
+       depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OVERO
+       select SND_OMAP_SOC_MCBSP
+       select SND_SOC_TWL4030
+       help
+         Say Y if you want to add support for SoC audio on the Gumstix Overo.
+
+config SND_OMAP_SOC_OMAP2EVM
+       tristate "SoC Audio support for OMAP2EVM board"
+       depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP2EVM
+       select SND_OMAP_SOC_MCBSP
+       select SND_SOC_TWL4030
+       help
+         Say Y if you want to add support for SoC audio on the omap2evm board.
+
+config SND_OMAP_SOC_SDP3430
+       tristate "SoC Audio support for Texas Instruments SDP3430"
+       depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_3430SDP
+       select SND_OMAP_SOC_MCBSP
+       select SND_SOC_TWL4030
+       help
+         Say Y if you want to add support for SoC audio on Texas Instruments
+         SDP3430.
+
+config SND_OMAP_SOC_OMAP3_PANDORA
+       tristate "SoC Audio support for OMAP3 Pandora"
+       depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3_PANDORA
+       select SND_OMAP_SOC_MCBSP
+       select SND_SOC_TWL4030
+       help
+         Say Y if you want to add support for SoC audio on the OMAP3 Pandora.
index e09d1f2..76fedd9 100644 (file)
@@ -8,6 +8,14 @@ obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
 # OMAP Machine Support
 snd-soc-n810-objs := n810.o
 snd-soc-osk5912-objs := osk5912.o
+snd-soc-overo-objs := overo.o
+snd-soc-omap2evm-objs := omap2evm.o
+snd-soc-sdp3430-objs := sdp3430.o
+snd-soc-omap3pandora-objs := omap3pandora.o
 
 obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
 obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
+obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o
+obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o
+obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
+obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
index fae3ad3..25593fe 100644 (file)
@@ -70,9 +70,13 @@ static void n810_ext_control(struct snd_soc_codec *codec)
 
 static int n810_startup(struct snd_pcm_substream *substream)
 {
+       struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_codec *codec = rtd->socdev->codec;
 
+       snd_pcm_hw_constraint_minmax(runtime,
+                                    SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+
        n810_ext_control(codec);
        return clk_enable(sys_clkout2);
 }
@@ -282,8 +286,9 @@ static struct snd_soc_dai_link n810_dai = {
 };
 
 /* Audio machine driver */
-static struct snd_soc_machine snd_soc_machine_n810 = {
+static struct snd_soc_card snd_soc_n810 = {
        .name = "N810",
+       .platform = &omap_soc_platform,
        .dai_link = &n810_dai,
        .num_links = 1,
 };
@@ -298,8 +303,7 @@ static struct aic3x_setup_data n810_aic33_setup = {
 
 /* Audio subsystem */
 static struct snd_soc_device n810_snd_devdata = {
-       .machine = &snd_soc_machine_n810,
-       .platform = &omap_soc_platform,
+       .card = &snd_soc_n810,
        .codec_dev = &soc_codec_dev_aic3x,
        .codec_data = &n810_aic33_setup,
 };
index 8485a8a..ec5e18a 100644 (file)
@@ -36,9 +36,7 @@
 #include "omap-mcbsp.h"
 #include "omap-pcm.h"
 
-#define OMAP_MCBSP_RATES       (SNDRV_PCM_RATE_44100 | \
-                                SNDRV_PCM_RATE_48000 | \
-                                SNDRV_PCM_RATE_KNOT)
+#define OMAP_MCBSP_RATES       (SNDRV_PCM_RATE_8000_96000)
 
 struct omap_mcbsp_data {
        unsigned int                    bus_id;
@@ -140,7 +138,8 @@ static const unsigned long omap34xx_mcbsp_port[][2] = {
 static const unsigned long omap34xx_mcbsp_port[][2] = {};
 #endif
 
-static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream)
+static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
+                                 struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -153,7 +152,8 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream)
        return err;
 }
 
-static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream)
+static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream,
+                                   struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -165,7 +165,8 @@ static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream)
        }
 }
 
-static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+                                 struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -194,14 +195,15 @@ static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd)
 }
 
 static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
-                                   struct snd_pcm_hw_params *params)
+                                   struct snd_pcm_hw_params *params,
+                                   struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
        struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
        struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
        int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
-       int wlen;
+       int wlen, channels;
        unsigned long port;
 
        if (cpu_class_is_omap1()) {
@@ -230,12 +232,17 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
                return 0;
        }
 
-       switch (params_channels(params)) {
+       channels = params_channels(params);
+       switch (channels) {
        case 2:
-               /* Set 1 word per (McBPSP) frame and use dual-phase frames */
-               regs->rcr2      |= RFRLEN2(1 - 1) | RPHASE;
+               /* Use dual-phase frames */
+               regs->rcr2      |= RPHASE;
+               regs->xcr2      |= XPHASE;
+       case 1:
+               /* Set 1 word per (McBSP) frame */
+               regs->rcr2      |= RFRLEN2(1 - 1);
                regs->rcr1      |= RFRLEN1(1 - 1);
-               regs->xcr2      |= XFRLEN2(1 - 1) | XPHASE;
+               regs->xcr2      |= XFRLEN2(1 - 1);
                regs->xcr1      |= XFRLEN1(1 - 1);
                break;
        default:
@@ -263,9 +270,9 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
                regs->srgr2     |= FPER(wlen * 2 - 1);
                regs->srgr1     |= FWID(wlen - 1);
                break;
-       case SND_SOC_DAIFMT_DSP_A:
-               regs->srgr2     |= FPER(wlen * 2 - 1);
-               regs->srgr1     |= FWID(wlen * 2 - 2);
+       case SND_SOC_DAIFMT_DSP_B:
+               regs->srgr2     |= FPER(wlen * channels - 1);
+               regs->srgr1     |= FWID(wlen * channels - 2);
                break;
        }
 
@@ -302,7 +309,7 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                regs->rcr2      |= RDATDLY(1);
                regs->xcr2      |= XDATDLY(1);
                break;
-       case SND_SOC_DAIFMT_DSP_A:
+       case SND_SOC_DAIFMT_DSP_B:
                /* 0-bit data delay */
                regs->rcr2      |= RDATDLY(0);
                regs->xcr2      |= XDATDLY(0);
@@ -452,17 +459,16 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
 
 #define OMAP_MCBSP_DAI_BUILDER(link_id)                                \
 {                                                              \
-       .name = "omap-mcbsp-dai-(link_id)",                     \
+       .name = "omap-mcbsp-dai-"#link_id,                      \
        .id = (link_id),                                        \
-       .type = SND_SOC_DAI_I2S,                                \
        .playback = {                                           \
-               .channels_min = 2,                              \
+               .channels_min = 1,                              \
                .channels_max = 2,                              \
                .rates = OMAP_MCBSP_RATES,                      \
                .formats = SNDRV_PCM_FMTBIT_S16_LE,             \
        },                                                      \
        .capture = {                                            \
-               .channels_min = 2,                              \
+               .channels_min = 1,                              \
                .channels_max = 2,                              \
                .rates = OMAP_MCBSP_RATES,                      \
                .formats = SNDRV_PCM_FMTBIT_S16_LE,             \
@@ -472,8 +478,6 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
                .shutdown = omap_mcbsp_dai_shutdown,            \
                .trigger = omap_mcbsp_dai_trigger,              \
                .hw_params = omap_mcbsp_dai_hw_params,          \
-       },                                                      \
-       .dai_ops = {                                            \
                .set_fmt = omap_mcbsp_dai_set_dai_fmt,          \
                .set_clkdiv = omap_mcbsp_dai_set_clkdiv,        \
                .set_sysclk = omap_mcbsp_dai_set_dai_sysclk,    \
@@ -495,6 +499,19 @@ struct snd_soc_dai omap_mcbsp_dai[] = {
 
 EXPORT_SYMBOL_GPL(omap_mcbsp_dai);
 
+static int __init snd_omap_mcbsp_init(void)
+{
+       return snd_soc_register_dais(omap_mcbsp_dai,
+                                    ARRAY_SIZE(omap_mcbsp_dai));
+}
+module_init(snd_omap_mcbsp_init);
+
+static void __exit snd_omap_mcbsp_exit(void)
+{
+       snd_soc_unregister_dais(omap_mcbsp_dai, ARRAY_SIZE(omap_mcbsp_dai));
+}
+module_exit(snd_omap_mcbsp_exit);
+
 MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>");
 MODULE_DESCRIPTION("OMAP I2S SoC Interface");
 MODULE_LICENSE("GPL");
index acd68ef..b0362df 100644 (file)
@@ -97,7 +97,7 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
        prtd->dma_data = dma_data;
        err = omap_request_dma(dma_data->dma_req, dma_data->name,
                               omap_pcm_dma_irq, substream, &prtd->dma_ch);
-       if (!err & !cpu_is_omap1510()) {
+       if (!err && !cpu_is_omap1510()) {
                /*
                 * Link channel with itself so DMA doesn't need any
                 * reprogramming while looping the buffer
@@ -354,6 +354,18 @@ struct snd_soc_platform omap_soc_platform = {
 };
 EXPORT_SYMBOL_GPL(omap_soc_platform);
 
+static int __init omap_soc_platform_init(void)
+{
+       return snd_soc_register_platform(&omap_soc_platform);
+}
+module_init(omap_soc_platform_init);
+
+static void __exit omap_soc_platform_exit(void)
+{
+       snd_soc_unregister_platform(&omap_soc_platform);
+}
+module_exit(omap_soc_platform_exit);
+
 MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>");
 MODULE_DESCRIPTION("OMAP PCM DMA module");
 MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap2evm.c b/sound/soc/omap/omap2evm.c
new file mode 100644 (file)
index 0000000..0c2322d
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * omap2evm.c  --  SoC audio machine driver for omap2evm board
+ *
+ * Author: Arun KS <arunks@mistralsolutions.com>
+ *
+ * Based on sound/soc/omap/overo.c by Steve Sakoman
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+static int omap2evm_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params,
+       struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       int ret;
+
+       /* Set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec DAI configuration\n");
+               return ret;
+       }
+
+       /* Set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set cpu DAI configuration\n");
+               return ret;
+       }
+
+       /* Set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+                                           SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec system clock\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops omap2evm_ops = {
+       .hw_params = omap2evm_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link omap2evm_dai = {
+       .name = "TWL4030",
+       .stream_name = "TWL4030",
+       .cpu_dai = &omap_mcbsp_dai[0],
+       .codec_dai = &twl4030_dai,
+       .ops = &omap2evm_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_omap2evm = {
+       .name = "omap2evm",
+       .platform = &omap_soc_platform,
+       .dai_link = &omap2evm_dai,
+       .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device omap2evm_snd_devdata = {
+       .card = &snd_soc_omap2evm,
+       .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *omap2evm_snd_device;
+
+static int __init omap2evm_soc_init(void)
+{
+       int ret;
+
+       if (!machine_is_omap2evm()) {
+               pr_debug("Not omap2evm!\n");
+               return -ENODEV;
+       }
+       printk(KERN_INFO "omap2evm SoC init\n");
+
+       omap2evm_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!omap2evm_snd_device) {
+               printk(KERN_ERR "Platform device allocation failed\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(omap2evm_snd_device, &omap2evm_snd_devdata);
+       omap2evm_snd_devdata.dev = &omap2evm_snd_device->dev;
+       *(unsigned int *)omap2evm_dai.cpu_dai->private_data = 1; /* McBSP2 */
+
+       ret = platform_device_add(omap2evm_snd_device);
+       if (ret)
+               goto err1;
+
+       return 0;
+
+err1:
+       printk(KERN_ERR "Unable to add platform device\n");
+       platform_device_put(omap2evm_snd_device);
+
+       return ret;
+}
+module_init(omap2evm_soc_init);
+
+static void __exit omap2evm_soc_exit(void)
+{
+       platform_device_unregister(omap2evm_snd_device);
+}
+module_exit(omap2evm_soc_exit);
+
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_DESCRIPTION("ALSA SoC omap2evm");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap3beagle.c b/sound/soc/omap/omap3beagle.c
new file mode 100644 (file)
index 0000000..fd24a4a
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * omap3beagle.c  --  SoC audio for OMAP3 Beagle
+ *
+ * Author: Steve Sakoman <steve@sakoman.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+static int omap3beagle_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       int ret;
+
+       /* Set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec DAI configuration\n");
+               return ret;
+       }
+
+       /* Set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set cpu DAI configuration\n");
+               return ret;
+       }
+
+       /* Set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+                                    SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec system clock\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops omap3beagle_ops = {
+       .hw_params = omap3beagle_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link omap3beagle_dai = {
+       .name = "TWL4030",
+       .stream_name = "TWL4030",
+       .cpu_dai = &omap_mcbsp_dai[0],
+       .codec_dai = &twl4030_dai,
+       .ops = &omap3beagle_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_omap3beagle = {
+       .name = "omap3beagle",
+       .platform = &omap_soc_platform,
+       .dai_link = &omap3beagle_dai,
+       .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device omap3beagle_snd_devdata = {
+       .card = &snd_soc_omap3beagle,
+       .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *omap3beagle_snd_device;
+
+static int __init omap3beagle_soc_init(void)
+{
+       int ret;
+
+       if (!machine_is_omap3_beagle()) {
+               pr_debug("Not OMAP3 Beagle!\n");
+               return -ENODEV;
+       }
+       pr_info("OMAP3 Beagle SoC init\n");
+
+       omap3beagle_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!omap3beagle_snd_device) {
+               printk(KERN_ERR "Platform device allocation failed\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(omap3beagle_snd_device, &omap3beagle_snd_devdata);
+       omap3beagle_snd_devdata.dev = &omap3beagle_snd_device->dev;
+       *(unsigned int *)omap3beagle_dai.cpu_dai->private_data = 1; /* McBSP2 */
+
+       ret = platform_device_add(omap3beagle_snd_device);
+       if (ret)
+               goto err1;
+
+       return 0;
+
+err1:
+       printk(KERN_ERR "Unable to add platform device\n");
+       platform_device_put(omap3beagle_snd_device);
+
+       return ret;
+}
+
+static void __exit omap3beagle_soc_exit(void)
+{
+       platform_device_unregister(omap3beagle_snd_device);
+}
+
+module_init(omap3beagle_soc_init);
+module_exit(omap3beagle_soc_exit);
+
+MODULE_AUTHOR("Steve Sakoman <steve@sakoman.com>");
+MODULE_DESCRIPTION("ALSA SoC OMAP3 Beagle");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c
new file mode 100644 (file)
index 0000000..bd91594
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * omap3pandora.c  --  SoC audio for Pandora Handheld Console
+ *
+ * Author: Gražvydas Ignotas <notasas@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+#define OMAP3_PANDORA_DAC_POWER_GPIO   118
+#define OMAP3_PANDORA_AMP_POWER_GPIO   14
+
+#define PREFIX "ASoC omap3pandora: "
+
+static int omap3pandora_cmn_hw_params(struct snd_soc_dai *codec_dai,
+       struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+       int ret;
+
+       /* Set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+       if (ret < 0) {
+               pr_err(PREFIX "can't set codec DAI configuration\n");
+               return ret;
+       }
+
+       /* Set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+       if (ret < 0) {
+               pr_err(PREFIX "can't set cpu DAI configuration\n");
+               return ret;
+       }
+
+       /* Set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+                                           SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               pr_err(PREFIX "can't set codec system clock\n");
+               return ret;
+       }
+
+       /* Set McBSP clock to external */
+       ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT, 0,
+                                           SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               pr_err(PREFIX "can't set cpu system clock\n");
+               return ret;
+       }
+
+       ret = snd_soc_dai_set_clkdiv(cpu_dai, OMAP_MCBSP_CLKGDV, 8);
+       if (ret < 0) {
+               pr_err(PREFIX "can't set SRG clock divider\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int omap3pandora_out_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+       return omap3pandora_cmn_hw_params(codec_dai, cpu_dai,
+                                         SND_SOC_DAIFMT_I2S |
+                                         SND_SOC_DAIFMT_IB_NF |
+                                         SND_SOC_DAIFMT_CBS_CFS);
+}
+
+static int omap3pandora_in_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+       return omap3pandora_cmn_hw_params(codec_dai, cpu_dai,
+                                         SND_SOC_DAIFMT_I2S |
+                                         SND_SOC_DAIFMT_NB_NF |
+                                         SND_SOC_DAIFMT_CBS_CFS);
+}
+
+static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *k, int event)
+{
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 1);
+               gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 1);
+       } else {
+               gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
+               mdelay(1);
+               gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
+       }
+
+       return 0;
+}
+
+/*
+ * Audio paths on Pandora board:
+ *
+ *  |O| ---> PCM DAC +-> AMP -> Headphone Jack
+ *  |M|         A    +--------> Line Out
+ *  |A| <~~clk~~+
+ *  |P| <--- TWL4030 <--------- Line In and MICs
+ */
+static const struct snd_soc_dapm_widget omap3pandora_out_dapm_widgets[] = {
+       SND_SOC_DAPM_DAC("PCM DAC", "Playback", SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM,
+                          0, 0, NULL, 0, omap3pandora_hp_event,
+                          SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+       SND_SOC_DAPM_LINE("Line Out", NULL),
+};
+
+static const struct snd_soc_dapm_widget omap3pandora_in_dapm_widgets[] = {
+       SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
+       SND_SOC_DAPM_MIC("Mic (external)", NULL),
+       SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route omap3pandora_out_map[] = {
+       {"Headphone Amplifier", NULL, "PCM DAC"},
+       {"Line Out", NULL, "PCM DAC"},
+       {"Headphone Jack", NULL, "Headphone Amplifier"},
+};
+
+static const struct snd_soc_dapm_route omap3pandora_in_map[] = {
+       {"INL", NULL, "Line In"},
+       {"INR", NULL, "Line In"},
+       {"INL", NULL, "Mic (Internal)"},
+       {"INR", NULL, "Mic (external)"},
+};
+
+static int omap3pandora_out_init(struct snd_soc_codec *codec)
+{
+       int ret;
+
+       ret = snd_soc_dapm_new_controls(codec, omap3pandora_out_dapm_widgets,
+                               ARRAY_SIZE(omap3pandora_out_dapm_widgets));
+       if (ret < 0)
+               return ret;
+
+       snd_soc_dapm_add_routes(codec, omap3pandora_out_map,
+               ARRAY_SIZE(omap3pandora_out_map));
+
+       return snd_soc_dapm_sync(codec);
+}
+
+static int omap3pandora_in_init(struct snd_soc_codec *codec)
+{
+       int ret;
+
+       ret = snd_soc_dapm_new_controls(codec, omap3pandora_in_dapm_widgets,
+                               ARRAY_SIZE(omap3pandora_in_dapm_widgets));
+       if (ret < 0)
+               return ret;
+
+       snd_soc_dapm_add_routes(codec, omap3pandora_in_map,
+               ARRAY_SIZE(omap3pandora_in_map));
+
+       return snd_soc_dapm_sync(codec);
+}
+
+static struct snd_soc_ops omap3pandora_out_ops = {
+       .hw_params = omap3pandora_out_hw_params,
+};
+
+static struct snd_soc_ops omap3pandora_in_ops = {
+       .hw_params = omap3pandora_in_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link omap3pandora_dai[] = {
+       {
+               .name = "PCM1773",
+               .stream_name = "HiFi Out",
+               .cpu_dai = &omap_mcbsp_dai[0],
+               .codec_dai = &twl4030_dai,
+               .ops = &omap3pandora_out_ops,
+               .init = omap3pandora_out_init,
+       }, {
+               .name = "TWL4030",
+               .stream_name = "Line/Mic In",
+               .cpu_dai = &omap_mcbsp_dai[1],
+               .codec_dai = &twl4030_dai,
+               .ops = &omap3pandora_in_ops,
+               .init = omap3pandora_in_init,
+       }
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_omap3pandora = {
+       .name = "omap3pandora",
+       .platform = &omap_soc_platform,
+       .dai_link = omap3pandora_dai,
+       .num_links = ARRAY_SIZE(omap3pandora_dai),
+};
+
+/* Audio subsystem */
+static struct snd_soc_device omap3pandora_snd_data = {
+       .card = &snd_soc_card_omap3pandora,
+       .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *omap3pandora_snd_device;
+
+static int __init omap3pandora_soc_init(void)
+{
+       int ret;
+
+       if (!machine_is_omap3_pandora()) {
+               pr_debug(PREFIX "Not OMAP3 Pandora\n");
+               return -ENODEV;
+       }
+       pr_info("OMAP3 Pandora SoC init\n");
+
+       ret = gpio_request(OMAP3_PANDORA_DAC_POWER_GPIO, "dac_power");
+       if (ret) {
+               pr_err(PREFIX "Failed to get DAC power GPIO\n");
+               return ret;
+       }
+
+       ret = gpio_direction_output(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
+       if (ret) {
+               pr_err(PREFIX "Failed to set DAC power GPIO direction\n");
+               goto fail0;
+       }
+
+       ret = gpio_request(OMAP3_PANDORA_AMP_POWER_GPIO, "amp_power");
+       if (ret) {
+               pr_err(PREFIX "Failed to get amp power GPIO\n");
+               goto fail0;
+       }
+
+       ret = gpio_direction_output(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
+       if (ret) {
+               pr_err(PREFIX "Failed to set amp power GPIO direction\n");
+               goto fail1;
+       }
+
+       omap3pandora_snd_device = platform_device_alloc("soc-audio", -1);
+       if (omap3pandora_snd_device == NULL) {
+               pr_err(PREFIX "Platform device allocation failed\n");
+               ret = -ENOMEM;
+               goto fail1;
+       }
+
+       platform_set_drvdata(omap3pandora_snd_device, &omap3pandora_snd_data);
+       omap3pandora_snd_data.dev = &omap3pandora_snd_device->dev;
+       *(unsigned int *)omap_mcbsp_dai[0].private_data = 1; /* McBSP2 */
+       *(unsigned int *)omap_mcbsp_dai[1].private_data = 3; /* McBSP4 */
+
+       ret = platform_device_add(omap3pandora_snd_device);
+       if (ret) {
+               pr_err(PREFIX "Unable to add platform device\n");
+               goto fail2;
+       }
+
+       return 0;
+
+fail2:
+       platform_device_put(omap3pandora_snd_device);
+fail1:
+       gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
+fail0:
+       gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
+       return ret;
+}
+module_init(omap3pandora_soc_init);
+
+static void __exit omap3pandora_soc_exit(void)
+{
+       platform_device_unregister(omap3pandora_snd_device);
+       gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
+       gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
+}
+module_exit(omap3pandora_soc_exit);
+
+MODULE_AUTHOR("Grazvydas Ignotas <notasas@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC OMAP3 Pandora");
+MODULE_LICENSE("GPL");
index 0fe7337..cd41a94 100644 (file)
@@ -61,7 +61,7 @@ static int osk_hw_params(struct snd_pcm_substream *substream,
 
        /* Set codec DAI configuration */
        err = snd_soc_dai_set_fmt(codec_dai,
-                                 SND_SOC_DAIFMT_DSP_A |
+                                 SND_SOC_DAIFMT_DSP_B |
                                  SND_SOC_DAIFMT_NB_IF |
                                  SND_SOC_DAIFMT_CBM_CFM);
        if (err < 0) {
@@ -71,7 +71,7 @@ static int osk_hw_params(struct snd_pcm_substream *substream,
 
        /* Set cpu DAI configuration */
        err = snd_soc_dai_set_fmt(cpu_dai,
-                                 SND_SOC_DAIFMT_DSP_A |
+                                 SND_SOC_DAIFMT_DSP_B |
                                  SND_SOC_DAIFMT_NB_IF |
                                  SND_SOC_DAIFMT_CBM_CFM);
        if (err < 0) {
@@ -143,16 +143,16 @@ static struct snd_soc_dai_link osk_dai = {
 };
 
 /* Audio machine driver */
-static struct snd_soc_machine snd_soc_machine_osk = {
+static struct snd_soc_card snd_soc_card_osk = {
        .name = "OSK5912",
+       .platform = &omap_soc_platform,
        .dai_link = &osk_dai,
        .num_links = 1,
 };
 
 /* Audio subsystem */
 static struct snd_soc_device osk_snd_devdata = {
-       .machine = &snd_soc_machine_osk,
-       .platform = &omap_soc_platform,
+       .card = &snd_soc_card_osk,
        .codec_dev = &soc_codec_dev_tlv320aic23,
 };
 
diff --git a/sound/soc/omap/overo.c b/sound/soc/omap/overo.c
new file mode 100644 (file)
index 0000000..a72dc4e
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * overo.c  --  SoC audio for Gumstix Overo
+ *
+ * Author: Steve Sakoman <steve@sakoman.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+static int overo_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       int ret;
+
+       /* Set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec DAI configuration\n");
+               return ret;
+       }
+
+       /* Set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set cpu DAI configuration\n");
+               return ret;
+       }
+
+       /* Set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+                                           SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec system clock\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops overo_ops = {
+       .hw_params = overo_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link overo_dai = {
+       .name = "TWL4030",
+       .stream_name = "TWL4030",
+       .cpu_dai = &omap_mcbsp_dai[0],
+       .codec_dai = &twl4030_dai,
+       .ops = &overo_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_card_overo = {
+       .name = "overo",
+       .platform = &omap_soc_platform,
+       .dai_link = &overo_dai,
+       .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device overo_snd_devdata = {
+       .card = &snd_soc_card_overo,
+       .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *overo_snd_device;
+
+static int __init overo_soc_init(void)
+{
+       int ret;
+
+       if (!machine_is_overo()) {
+               pr_debug("Not Overo!\n");
+               return -ENODEV;
+       }
+       printk(KERN_INFO "overo SoC init\n");
+
+       overo_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!overo_snd_device) {
+               printk(KERN_ERR "Platform device allocation failed\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(overo_snd_device, &overo_snd_devdata);
+       overo_snd_devdata.dev = &overo_snd_device->dev;
+       *(unsigned int *)overo_dai.cpu_dai->private_data = 1; /* McBSP2 */
+
+       ret = platform_device_add(overo_snd_device);
+       if (ret)
+               goto err1;
+
+       return 0;
+
+err1:
+       printk(KERN_ERR "Unable to add platform device\n");
+       platform_device_put(overo_snd_device);
+
+       return ret;
+}
+module_init(overo_soc_init);
+
+static void __exit overo_soc_exit(void)
+{
+       platform_device_unregister(overo_snd_device);
+}
+module_exit(overo_soc_exit);
+
+MODULE_AUTHOR("Steve Sakoman <steve@sakoman.com>");
+MODULE_DESCRIPTION("ALSA SoC overo");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c
new file mode 100644 (file)
index 0000000..ad97836
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * sdp3430.c  --  SoC audio for TI OMAP3430 SDP
+ *
+ * Author: Misael Lopez Cruz <x0052729@ti.com>
+ *
+ * Based on:
+ * Author: Steve Sakoman <steve@sakoman.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+static int sdp3430_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       int ret;
+
+       /* Set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec DAI configuration\n");
+               return ret;
+       }
+
+       /* Set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set cpu DAI configuration\n");
+               return ret;
+       }
+
+       /* Set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+                                           SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec system clock\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops sdp3430_ops = {
+       .hw_params = sdp3430_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link sdp3430_dai = {
+       .name = "TWL4030",
+       .stream_name = "TWL4030",
+       .cpu_dai = &omap_mcbsp_dai[0],
+       .codec_dai = &twl4030_dai,
+       .ops = &sdp3430_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_machine snd_soc_machine_sdp3430 = {
+       .name = "SDP3430",
+       .platform = &omap_soc_platform,
+       .dai_link = &sdp3430_dai,
+       .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device sdp3430_snd_devdata = {
+       .machine = &snd_soc_machine_sdp3430,
+       .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *sdp3430_snd_device;
+
+static int __init sdp3430_soc_init(void)
+{
+       int ret;
+
+       if (!machine_is_omap_3430sdp()) {
+               pr_debug("Not SDP3430!\n");
+               return -ENODEV;
+       }
+       printk(KERN_INFO "SDP3430 SoC init\n");
+
+       sdp3430_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!sdp3430_snd_device) {
+               printk(KERN_ERR "Platform device allocation failed\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(sdp3430_snd_device, &sdp3430_snd_devdata);
+       sdp3430_snd_devdata.dev = &sdp3430_snd_device->dev;
+       *(unsigned int *)sdp3430_dai.cpu_dai->private_data = 1; /* McBSP2 */
+
+       ret = platform_device_add(sdp3430_snd_device);
+       if (ret)
+               goto err1;
+
+       return 0;
+
+err1:
+       printk(KERN_ERR "Unable to add platform device\n");
+       platform_device_put(sdp3430_snd_device);
+
+       return ret;
+}
+module_init(sdp3430_soc_init);
+
+static void __exit sdp3430_soc_exit(void)
+{
+       platform_device_unregister(sdp3430_snd_device);
+}
+module_exit(sdp3430_soc_exit);
+
+MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>");
+MODULE_DESCRIPTION("ALSA SoC SDP3430");
+MODULE_LICENSE("GPL");
+
index f8c1cdd..f82e106 100644 (file)
@@ -21,6 +21,9 @@ config SND_PXA2XX_SOC_AC97
 config SND_PXA2XX_SOC_I2S
        tristate
 
+config SND_PXA_SOC_SSP
+       tristate
+
 config SND_PXA2XX_SOC_CORGI
        tristate "SoC Audio support for Sharp Zaurus SL-C7x0"
        depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx
@@ -75,3 +78,22 @@ config SND_PXA2XX_SOC_EM_X270
        help
          Say Y if you want to add support for SoC audio on
          CompuLab EM-x270.
+
+config SND_PXA2XX_SOC_PALM27X
+       bool "SoC Audio support for Palm T|X, T5 and LifeDrive"
+       depends on SND_PXA2XX_SOC && (MACH_PALMLD || MACH_PALMTX || MACH_PALMT5)
+       select SND_PXA2XX_SOC_AC97
+       select SND_SOC_WM9712
+       help
+         Say Y if you want to add support for SoC audio on
+         Palm T|X, T5 or LifeDrive handheld computer.
+
+config SND_SOC_ZYLONITE
+       tristate "SoC Audio support for Marvell Zylonite"
+       depends on SND_PXA2XX_SOC && MACH_ZYLONITE
+       select SND_PXA2XX_SOC_AC97
+       select SND_PXA_SOC_SSP
+       select SND_SOC_WM9713
+       help
+         Say Y if you want to add support for SoC audio on the
+         Marvell Zylonite reference platform.
index 5bc8edf..08a9f27 100644 (file)
@@ -2,10 +2,12 @@
 snd-soc-pxa2xx-objs := pxa2xx-pcm.o
 snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o
 snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o
+snd-soc-pxa-ssp-objs := pxa-ssp.o
 
 obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o
 obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o
 obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o
+obj-$(CONFIG_SND_PXA_SOC_SSP) += snd-soc-pxa-ssp.o
 
 # PXA Machine Support
 snd-soc-corgi-objs := corgi.o
@@ -14,6 +16,8 @@ snd-soc-tosa-objs := tosa.o
 snd-soc-e800-objs := e800_wm9712.o
 snd-soc-spitz-objs := spitz.o
 snd-soc-em-x270-objs := em-x270.o
+snd-soc-palm27x-objs := palm27x.o
+snd-soc-zylonite-objs := zylonite.o
 
 obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
 obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
@@ -21,3 +25,5 @@ obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o
 obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o
 obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
 obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
+obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o
+obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o
index 2718eaf..1ba25a5 100644 (file)
@@ -108,15 +108,11 @@ static int corgi_startup(struct snd_pcm_substream *substream)
 }
 
 /* we need to unmute the HP at shutdown as the mute burns power on corgi */
-static int corgi_shutdown(struct snd_pcm_substream *substream)
+static void corgi_shutdown(struct snd_pcm_substream *substream)
 {
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_codec *codec = rtd->socdev->codec;
-
        /* set = unmute headphone */
        gpio_set_value(CORGI_GPIO_MUTE_L, 1);
        gpio_set_value(CORGI_GPIO_MUTE_R, 1);
-       return 0;
 }
 
 static int corgi_hw_params(struct snd_pcm_substream *substream,
@@ -314,8 +310,9 @@ static struct snd_soc_dai_link corgi_dai = {
 };
 
 /* corgi audio machine driver */
-static struct snd_soc_machine snd_soc_machine_corgi = {
+static struct snd_soc_card snd_soc_corgi = {
        .name = "Corgi",
+       .platform = &pxa2xx_soc_platform,
        .dai_link = &corgi_dai,
        .num_links = 1,
 };
@@ -328,8 +325,7 @@ static struct wm8731_setup_data corgi_wm8731_setup = {
 
 /* corgi audio subsystem */
 static struct snd_soc_device corgi_snd_devdata = {
-       .machine = &snd_soc_machine_corgi,
-       .platform = &pxa2xx_soc_platform,
+       .card = &snd_soc_corgi,
        .codec_dev = &soc_codec_dev_wm8731,
        .codec_data = &corgi_wm8731_setup,
 };
index 6781c5b..2e3386d 100644 (file)
@@ -29,7 +29,7 @@
 #include "pxa2xx-pcm.h"
 #include "pxa2xx-ac97.h"
 
-static struct snd_soc_machine e800;
+static struct snd_soc_card e800;
 
 static struct snd_soc_dai_link e800_dai[] = {
 {
@@ -40,15 +40,15 @@ static struct snd_soc_dai_link e800_dai[] = {
 },
 };
 
-static struct snd_soc_machine e800 = {
+static struct snd_soc_card e800 = {
        .name = "Toshiba e800",
+       .platform = &pxa2xx_soc_platform,
        .dai_link = e800_dai,
        .num_links = ARRAY_SIZE(e800_dai),
 };
 
 static struct snd_soc_device e800_snd_devdata = {
-       .machine = &e800,
-       .platform = &pxa2xx_soc_platform,
+       .card = &e800,
        .codec_dev = &soc_codec_dev_wm9712,
 };
 
index e6ff692..fe4a729 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/moduleparam.h>
 #include <linux/device.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
@@ -53,15 +52,15 @@ static struct snd_soc_dai_link em_x270_dai[] = {
        },
 };
 
-static struct snd_soc_machine em_x270 = {
+static struct snd_soc_card em_x270 = {
        .name = "EM-X270",
+       .platform = &pxa2xx_soc_platform,
        .dai_link = em_x270_dai,
        .num_links = ARRAY_SIZE(em_x270_dai),
 };
 
 static struct snd_soc_device em_x270_snd_devdata = {
-       .machine = &em_x270,
-       .platform = &pxa2xx_soc_platform,
+       .card = &em_x270,
        .codec_dev = &soc_codec_dev_wm9712,
 };
 
diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c
new file mode 100644 (file)
index 0000000..4a9cf30
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * linux/sound/soc/pxa/palm27x.c
+ *
+ * SoC Audio driver for Palm T|X, T5 and LifeDrive
+ *
+ * based on tosa.c
+ *
+ * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/audio.h>
+#include <mach/palmasoc.h>
+
+#include "../codecs/wm9712.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+
+static int palm27x_jack_func = 1;
+static int palm27x_spk_func = 1;
+static int palm27x_ep_gpio = -1;
+
+static void palm27x_ext_control(struct snd_soc_codec *codec)
+{
+       if (!palm27x_spk_func)
+               snd_soc_dapm_enable_pin(codec, "Speaker");
+       else
+               snd_soc_dapm_disable_pin(codec, "Speaker");
+
+       if (!palm27x_jack_func)
+               snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+       else
+               snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+
+       snd_soc_dapm_sync(codec);
+}
+
+static int palm27x_startup(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_codec *codec = rtd->socdev->codec;
+
+       /* check the jack status at stream startup */
+       palm27x_ext_control(codec);
+       return 0;
+}
+
+static struct snd_soc_ops palm27x_ops = {
+       .startup = palm27x_startup,
+};
+
+static irqreturn_t palm27x_interrupt(int irq, void *v)
+{
+       palm27x_spk_func = gpio_get_value(palm27x_ep_gpio);
+       palm27x_jack_func = !palm27x_spk_func;
+       return IRQ_HANDLED;
+}
+
+static int palm27x_get_jack(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.integer.value[0] = palm27x_jack_func;
+       return 0;
+}
+
+static int palm27x_set_jack(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+
+       if (palm27x_jack_func == ucontrol->value.integer.value[0])
+               return 0;
+
+       palm27x_jack_func = ucontrol->value.integer.value[0];
+       palm27x_ext_control(codec);
+       return 1;
+}
+
+static int palm27x_get_spk(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.integer.value[0] = palm27x_spk_func;
+       return 0;
+}
+
+static int palm27x_set_spk(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+
+       if (palm27x_spk_func == ucontrol->value.integer.value[0])
+               return 0;
+
+       palm27x_spk_func = ucontrol->value.integer.value[0];
+       palm27x_ext_control(codec);
+       return 1;
+}
+
+/* PalmTX machine dapm widgets */
+static const struct snd_soc_dapm_widget palm27x_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+       SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+/* PalmTX audio map */
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* headphone connected to HPOUTL, HPOUTR */
+       {"Headphone Jack", NULL, "HPOUTL"},
+       {"Headphone Jack", NULL, "HPOUTR"},
+
+       /* ext speaker connected to ROUT2, LOUT2 */
+       {"Speaker", NULL, "LOUT2"},
+       {"Speaker", NULL, "ROUT2"},
+};
+
+static const char *jack_function[] = {"Headphone", "Off"};
+static const char *spk_function[] = {"On", "Off"};
+static const struct soc_enum palm27x_enum[] = {
+       SOC_ENUM_SINGLE_EXT(2, jack_function),
+       SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static const struct snd_kcontrol_new palm27x_controls[] = {
+       SOC_ENUM_EXT("Jack Function", palm27x_enum[0], palm27x_get_jack,
+               palm27x_set_jack),
+       SOC_ENUM_EXT("Speaker Function", palm27x_enum[1], palm27x_get_spk,
+               palm27x_set_spk),
+};
+
+static int palm27x_ac97_init(struct snd_soc_codec *codec)
+{
+       int i, err;
+
+       snd_soc_dapm_nc_pin(codec, "OUT3");
+       snd_soc_dapm_nc_pin(codec, "MONOOUT");
+
+       /* add palm27x specific controls */
+       for (i = 0; i < ARRAY_SIZE(palm27x_controls); i++) {
+               err = snd_ctl_add(codec->card,
+                               snd_soc_cnew(&palm27x_controls[i],
+                                               codec, NULL));
+               if (err < 0)
+                       return err;
+       }
+
+       /* add palm27x specific widgets */
+       snd_soc_dapm_new_controls(codec, palm27x_dapm_widgets,
+                               ARRAY_SIZE(palm27x_dapm_widgets));
+
+       /* set up palm27x specific audio path audio_map */
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       snd_soc_dapm_sync(codec);
+       return 0;
+}
+
+static struct snd_soc_dai_link palm27x_dai[] = {
+{
+       .name = "AC97 HiFi",
+       .stream_name = "AC97 HiFi",
+       .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
+       .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
+       .init = palm27x_ac97_init,
+       .ops = &palm27x_ops,
+},
+{
+       .name = "AC97 Aux",
+       .stream_name = "AC97 Aux",
+       .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
+       .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
+       .ops = &palm27x_ops,
+},
+};
+
+static struct snd_soc_card palm27x_asoc = {
+       .name = "Palm/PXA27x",
+       .platform = &pxa2xx_soc_platform,
+       .dai_link = palm27x_dai,
+       .num_links = ARRAY_SIZE(palm27x_dai),
+};
+
+static struct snd_soc_device palm27x_snd_devdata = {
+       .card = &palm27x_asoc,
+       .codec_dev = &soc_codec_dev_wm9712,
+};
+
+static struct platform_device *palm27x_snd_device;
+
+static int __init palm27x_asoc_init(void)
+{
+       int ret;
+
+       if (!(machine_is_palmtx() || machine_is_palmt5() ||
+               machine_is_palmld()))
+               return -ENODEV;
+
+       ret = gpio_request(palm27x_ep_gpio, "Headphone Jack");
+       if (ret)
+               return ret;
+       ret = gpio_direction_input(palm27x_ep_gpio);
+       if (ret)
+               goto err_alloc;
+
+       if (request_irq(gpio_to_irq(palm27x_ep_gpio), palm27x_interrupt,
+                       IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                       "Headphone jack", NULL))
+               goto err_alloc;
+
+       palm27x_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!palm27x_snd_device) {
+               ret = -ENOMEM;
+               goto err_dev;
+       }
+
+       platform_set_drvdata(palm27x_snd_device, &palm27x_snd_devdata);
+       palm27x_snd_devdata.dev = &palm27x_snd_device->dev;
+       ret = platform_device_add(palm27x_snd_device);
+
+       if (ret != 0)
+               goto put_device;
+
+       return 0;
+
+put_device:
+       platform_device_put(palm27x_snd_device);
+err_dev:
+       free_irq(gpio_to_irq(palm27x_ep_gpio), NULL);
+err_alloc:
+       gpio_free(palm27x_ep_gpio);
+
+       return ret;
+}
+
+static void __exit palm27x_asoc_exit(void)
+{
+       free_irq(gpio_to_irq(palm27x_ep_gpio), NULL);
+       gpio_free(palm27x_ep_gpio);
+       platform_device_unregister(palm27x_snd_device);
+}
+
+void __init palm27x_asoc_set_pdata(struct palm27x_asoc_info *data)
+{
+       palm27x_ep_gpio = data->jack_gpio;
+}
+
+module_init(palm27x_asoc_init);
+module_exit(palm27x_asoc_exit);
+
+/* Module information */
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC Palm T|X, T5 and LifeDrive");
+MODULE_LICENSE("GPL");
index 4d9930c..6e98271 100644 (file)
@@ -276,8 +276,9 @@ static struct snd_soc_dai_link poodle_dai = {
 };
 
 /* poodle audio machine driver */
-static struct snd_soc_machine snd_soc_machine_poodle = {
+static struct snd_soc_card snd_soc_poodle = {
        .name = "Poodle",
+       .platform = &pxa2xx_soc_platform,
        .dai_link = &poodle_dai,
        .num_links = 1,
 };
@@ -290,8 +291,7 @@ static struct wm8731_setup_data poodle_wm8731_setup = {
 
 /* poodle audio subsystem */
 static struct snd_soc_device poodle_snd_devdata = {
-       .machine = &snd_soc_machine_poodle,
-       .platform = &pxa2xx_soc_platform,
+       .card = &snd_soc_poodle,
        .codec_dev = &soc_codec_dev_wm8731,
        .codec_data = &poodle_wm8731_setup,
 };
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
new file mode 100644 (file)
index 0000000..73cb6b4
--- /dev/null
@@ -0,0 +1,931 @@
+#define DEBUG
+/*
+ * pxa-ssp.c  --  ALSA Soc Audio Layer
+ *
+ * Copyright 2005,2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ * TODO:
+ *  o Test network mode for > 16bit sample size
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/pxa2xx-lib.h>
+
+#include <mach/hardware.h>
+#include <mach/pxa-regs.h>
+#include <mach/regs-ssp.h>
+#include <mach/audio.h>
+#include <mach/ssp.h>
+
+#include "pxa2xx-pcm.h"
+#include "pxa-ssp.h"
+
+/*
+ * SSP audio private data
+ */
+struct ssp_priv {
+       struct ssp_dev dev;
+       unsigned int sysclk;
+       int dai_fmt;
+#ifdef CONFIG_PM
+       struct ssp_state state;
+#endif
+};
+
+#define PXA2xx_SSP1_BASE       0x41000000
+#define PXA27x_SSP2_BASE       0x41700000
+#define PXA27x_SSP3_BASE       0x41900000
+#define PXA3xx_SSP4_BASE       0x41a00000
+
+static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_mono_out = {
+       .name                   = "SSP1 PCM Mono out",
+       .dev_addr               = PXA2xx_SSP1_BASE + SSDR,
+       .drcmr                  = &DRCMR(14),
+       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+                                 DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_mono_in = {
+       .name                   = "SSP1 PCM Mono in",
+       .dev_addr               = PXA2xx_SSP1_BASE + SSDR,
+       .drcmr                  = &DRCMR(13),
+       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+                                 DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_stereo_out = {
+       .name                   = "SSP1 PCM Stereo out",
+       .dev_addr               = PXA2xx_SSP1_BASE + SSDR,
+       .drcmr                  = &DRCMR(14),
+       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+                                 DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_stereo_in = {
+       .name                   = "SSP1 PCM Stereo in",
+       .dev_addr               = PXA2xx_SSP1_BASE + SSDR,
+       .drcmr                  = &DRCMR(13),
+       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+                                 DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_mono_out = {
+       .name                   = "SSP2 PCM Mono out",
+       .dev_addr               = PXA27x_SSP2_BASE + SSDR,
+       .drcmr                  = &DRCMR(16),
+       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+                                 DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_mono_in = {
+       .name                   = "SSP2 PCM Mono in",
+       .dev_addr               = PXA27x_SSP2_BASE + SSDR,
+       .drcmr                  = &DRCMR(15),
+       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+                                 DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_stereo_out = {
+       .name                   = "SSP2 PCM Stereo out",
+       .dev_addr               = PXA27x_SSP2_BASE + SSDR,
+       .drcmr                  = &DRCMR(16),
+       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+                                 DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_stereo_in = {
+       .name                   = "SSP2 PCM Stereo in",
+       .dev_addr               = PXA27x_SSP2_BASE + SSDR,
+       .drcmr                  = &DRCMR(15),
+       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+                                 DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_mono_out = {
+       .name                   = "SSP3 PCM Mono out",
+       .dev_addr               = PXA27x_SSP3_BASE + SSDR,
+       .drcmr                  = &DRCMR(67),
+       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+                                 DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_mono_in = {
+       .name                   = "SSP3 PCM Mono in",
+       .dev_addr               = PXA27x_SSP3_BASE + SSDR,
+       .drcmr                  = &DRCMR(66),
+       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+                                 DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_stereo_out = {
+       .name                   = "SSP3 PCM Stereo out",
+       .dev_addr               = PXA27x_SSP3_BASE + SSDR,
+       .drcmr                  = &DRCMR(67),
+       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+                                 DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_stereo_in = {
+       .name                   = "SSP3 PCM Stereo in",
+       .dev_addr               = PXA27x_SSP3_BASE + SSDR,
+       .drcmr                  = &DRCMR(66),
+       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+                                 DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_mono_out = {
+       .name                   = "SSP4 PCM Mono out",
+       .dev_addr               = PXA3xx_SSP4_BASE + SSDR,
+       .drcmr                  = &DRCMR(67),
+       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+                                 DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_mono_in = {
+       .name                   = "SSP4 PCM Mono in",
+       .dev_addr               = PXA3xx_SSP4_BASE + SSDR,
+       .drcmr                  = &DRCMR(66),
+       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+                                 DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_stereo_out = {
+       .name                   = "SSP4 PCM Stereo out",
+       .dev_addr               = PXA3xx_SSP4_BASE + SSDR,
+       .drcmr                  = &DRCMR(67),
+       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+                                 DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_stereo_in = {
+       .name                   = "SSP4 PCM Stereo in",
+       .dev_addr               = PXA3xx_SSP4_BASE + SSDR,
+       .drcmr                  = &DRCMR(66),
+       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+                                 DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static void dump_registers(struct ssp_device *ssp)
+{
+       dev_dbg(&ssp->pdev->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n",
+                ssp_read_reg(ssp, SSCR0), ssp_read_reg(ssp, SSCR1),
+                ssp_read_reg(ssp, SSTO));
+
+       dev_dbg(&ssp->pdev->dev, "SSPSP 0x%08x SSSR 0x%08x SSACD 0x%08x\n",
+                ssp_read_reg(ssp, SSPSP), ssp_read_reg(ssp, SSSR),
+                ssp_read_reg(ssp, SSACD));
+}
+
+static struct pxa2xx_pcm_dma_params *ssp_dma_params[4][4] = {
+       {
+               &pxa_ssp1_pcm_mono_out, &pxa_ssp1_pcm_mono_in,
+               &pxa_ssp1_pcm_stereo_out, &pxa_ssp1_pcm_stereo_in,
+       },
+       {
+               &pxa_ssp2_pcm_mono_out, &pxa_ssp2_pcm_mono_in,
+               &pxa_ssp2_pcm_stereo_out, &pxa_ssp2_pcm_stereo_in,
+       },
+       {
+               &pxa_ssp3_pcm_mono_out, &pxa_ssp3_pcm_mono_in,
+               &pxa_ssp3_pcm_stereo_out, &pxa_ssp3_pcm_stereo_in,
+       },
+       {
+               &pxa_ssp4_pcm_mono_out, &pxa_ssp4_pcm_mono_in,
+               &pxa_ssp4_pcm_stereo_out, &pxa_ssp4_pcm_stereo_in,
+       },
+};
+
+static int pxa_ssp_startup(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       struct ssp_priv *priv = cpu_dai->private_data;
+       int ret = 0;
+
+       if (!cpu_dai->active) {
+               ret = ssp_init(&priv->dev, cpu_dai->id + 1, SSP_NO_IRQ);
+               if (ret < 0)
+                       return ret;
+               ssp_disable(&priv->dev);
+       }
+       return ret;
+}
+
+static void pxa_ssp_shutdown(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       struct ssp_priv *priv = cpu_dai->private_data;
+
+       if (!cpu_dai->active) {
+               ssp_disable(&priv->dev);
+               ssp_exit(&priv->dev);
+       }
+}
+
+#ifdef CONFIG_PM
+
+static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai)
+{
+       struct ssp_priv *priv = cpu_dai->private_data;
+
+       if (!cpu_dai->active)
+               return 0;
+
+       ssp_save_state(&priv->dev, &priv->state);
+       clk_disable(priv->dev.ssp->clk);
+       return 0;
+}
+
+static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai)
+{
+       struct ssp_priv *priv = cpu_dai->private_data;
+
+       if (!cpu_dai->active)
+               return 0;
+
+       clk_enable(priv->dev.ssp->clk);
+       ssp_restore_state(&priv->dev, &priv->state);
+       ssp_enable(&priv->dev);
+
+       return 0;
+}
+
+#else
+#define pxa_ssp_suspend        NULL
+#define pxa_ssp_resume NULL
+#endif
+
+/**
+ * ssp_set_clkdiv - set SSP clock divider
+ * @div: serial clock rate divider
+ */
+static void ssp_set_scr(struct ssp_dev *dev, u32 div)
+{
+       struct ssp_device *ssp = dev->ssp;
+       u32 sscr0 = ssp_read_reg(dev->ssp, SSCR0) & ~SSCR0_SCR;
+
+       ssp_write_reg(ssp, SSCR0, (sscr0 | SSCR0_SerClkDiv(div)));
+}
+
+/*
+ * Set the SSP ports SYSCLK.
+ */
+static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+       int clk_id, unsigned int freq, int dir)
+{
+       struct ssp_priv *priv = cpu_dai->private_data;
+       struct ssp_device *ssp = priv->dev.ssp;
+       int val;
+
+       u32 sscr0 = ssp_read_reg(ssp, SSCR0) &
+               ~(SSCR0_ECS |  SSCR0_NCS | SSCR0_MOD | SSCR0_ADC);
+
+       dev_dbg(&ssp->pdev->dev,
+               "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %d\n",
+               cpu_dai->id, clk_id, freq);
+
+       switch (clk_id) {
+       case PXA_SSP_CLK_NET_PLL:
+               sscr0 |= SSCR0_MOD;
+               break;
+       case PXA_SSP_CLK_PLL:
+               /* Internal PLL is fixed */
+               if (cpu_is_pxa25x())
+                       priv->sysclk = 1843200;
+               else
+                       priv->sysclk = 13000000;
+               break;
+       case PXA_SSP_CLK_EXT:
+               priv->sysclk = freq;
+               sscr0 |= SSCR0_ECS;
+               break;
+       case PXA_SSP_CLK_NET:
+               priv->sysclk = freq;
+               sscr0 |= SSCR0_NCS | SSCR0_MOD;
+               break;
+       case PXA_SSP_CLK_AUDIO:
+               priv->sysclk = 0;
+               ssp_set_scr(&priv->dev, 1);
+               sscr0 |= SSCR0_ADC;
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       /* The SSP clock must be disabled when changing SSP clock mode
+        * on PXA2xx.  On PXA3xx it must be enabled when doing so. */
+       if (!cpu_is_pxa3xx())
+               clk_disable(priv->dev.ssp->clk);
+       val = ssp_read_reg(ssp, SSCR0) | sscr0;
+       ssp_write_reg(ssp, SSCR0, val);
+       if (!cpu_is_pxa3xx())
+               clk_enable(priv->dev.ssp->clk);
+
+       return 0;
+}
+
+/*
+ * Set the SSP clock dividers.
+ */
+static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+       int div_id, int div)
+{
+       struct ssp_priv *priv = cpu_dai->private_data;
+       struct ssp_device *ssp = priv->dev.ssp;
+       int val;
+
+       switch (div_id) {
+       case PXA_SSP_AUDIO_DIV_ACDS:
+               val = (ssp_read_reg(ssp, SSACD) & ~0x7) | SSACD_ACDS(div);
+               ssp_write_reg(ssp, SSACD, val);
+               break;
+       case PXA_SSP_AUDIO_DIV_SCDB:
+               val = ssp_read_reg(ssp, SSACD);
+               val &= ~SSACD_SCDB;
+#if defined(CONFIG_PXA3xx)
+               if (cpu_is_pxa3xx())
+                       val &= ~SSACD_SCDX8;
+#endif
+               switch (div) {
+               case PXA_SSP_CLK_SCDB_1:
+                       val |= SSACD_SCDB;
+                       break;
+               case PXA_SSP_CLK_SCDB_4:
+                       break;
+#if defined(CONFIG_PXA3xx)
+               case PXA_SSP_CLK_SCDB_8:
+                       if (cpu_is_pxa3xx())
+                               val |= SSACD_SCDX8;
+                       else
+                               return -EINVAL;
+                       break;
+#endif
+               default:
+                       return -EINVAL;
+               }
+               ssp_write_reg(ssp, SSACD, val);
+               break;
+       case PXA_SSP_DIV_SCR:
+               ssp_set_scr(&priv->dev, div);
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+/*
+ * Configure the PLL frequency pxa27x and (afaik - pxa320 only)
+ */
+static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai,
+       int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+       struct ssp_priv *priv = cpu_dai->private_data;
+       struct ssp_device *ssp = priv->dev.ssp;
+       u32 ssacd = ssp_read_reg(ssp, SSACD) & ~0x70;
+
+#if defined(CONFIG_PXA3xx)
+       if (cpu_is_pxa3xx())
+               ssp_write_reg(ssp, SSACDD, 0);
+#endif
+
+       switch (freq_out) {
+       case 5622000:
+               break;
+       case 11345000:
+               ssacd |= (0x1 << 4);
+               break;
+       case 12235000:
+               ssacd |= (0x2 << 4);
+               break;
+       case 14857000:
+               ssacd |= (0x3 << 4);
+               break;
+       case 32842000:
+               ssacd |= (0x4 << 4);
+               break;
+       case 48000000:
+               ssacd |= (0x5 << 4);
+               break;
+       case 0:
+               /* Disable */
+               break;
+
+       default:
+#ifdef CONFIG_PXA3xx
+               /* PXA3xx has a clock ditherer which can be used to generate
+                * a wider range of frequencies - calculate a value for it.
+                */
+               if (cpu_is_pxa3xx()) {
+                       u32 val;
+                       u64 tmp = 19968;
+                       tmp *= 1000000;
+                       do_div(tmp, freq_out);
+                       val = tmp;
+
+                       val = (val << 16) | 64;;
+                       ssp_write_reg(ssp, SSACDD, val);
+
+                       ssacd |= (0x6 << 4);
+
+                       dev_dbg(&ssp->pdev->dev,
+                               "Using SSACDD %x to supply %dHz\n",
+                               val, freq_out);
+                       break;
+               }
+#endif
+
+               return -EINVAL;
+       }
+
+       ssp_write_reg(ssp, SSACD, ssacd);
+
+       return 0;
+}
+
+/*
+ * Set the active slots in TDM/Network mode
+ */
+static int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
+       unsigned int mask, int slots)
+{
+       struct ssp_priv *priv = cpu_dai->private_data;
+       struct ssp_device *ssp = priv->dev.ssp;
+       u32 sscr0;
+
+       sscr0 = ssp_read_reg(ssp, SSCR0) & ~SSCR0_SlotsPerFrm(7);
+
+       /* set number of active slots */
+       sscr0 |= SSCR0_SlotsPerFrm(slots);
+       ssp_write_reg(ssp, SSCR0, sscr0);
+
+       /* set active slot mask */
+       ssp_write_reg(ssp, SSTSA, mask);
+       ssp_write_reg(ssp, SSRSA, mask);
+       return 0;
+}
+
+/*
+ * Tristate the SSP DAI lines
+ */
+static int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai,
+       int tristate)
+{
+       struct ssp_priv *priv = cpu_dai->private_data;
+       struct ssp_device *ssp = priv->dev.ssp;
+       u32 sscr1;
+
+       sscr1 = ssp_read_reg(ssp, SSCR1);
+       if (tristate)
+               sscr1 &= ~SSCR1_TTE;
+       else
+               sscr1 |= SSCR1_TTE;
+       ssp_write_reg(ssp, SSCR1, sscr1);
+
+       return 0;
+}
+
+/*
+ * Set up the SSP DAI format.
+ * The SSP Port must be inactive before calling this function as the
+ * physical interface format is changed.
+ */
+static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+               unsigned int fmt)
+{
+       struct ssp_priv *priv = cpu_dai->private_data;
+       struct ssp_device *ssp = priv->dev.ssp;
+       u32 sscr0;
+       u32 sscr1;
+       u32 sspsp;
+
+       /* reset port settings */
+       sscr0 = ssp_read_reg(ssp, SSCR0) &
+               (SSCR0_ECS |  SSCR0_NCS | SSCR0_MOD | SSCR0_ADC);
+       sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7);
+       sspsp = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+               sscr1 |= SSCR1_SCLKDIR;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ssp_write_reg(ssp, SSCR0, sscr0);
+       ssp_write_reg(ssp, SSCR1, sscr1);
+       ssp_write_reg(ssp, SSPSP, sspsp);
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               sscr0 |= SSCR0_MOD | SSCR0_PSP;
+               sscr1 |= SSCR1_RWOT | SSCR1_TRAIL;
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       sspsp |= SSPSP_FSRT;
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       sspsp |= SSPSP_SFRMP | SSPSP_FSRT;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       sspsp |= SSPSP_SFRMP;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+
+       case SND_SOC_DAIFMT_DSP_A:
+               sspsp |= SSPSP_FSRT;
+       case SND_SOC_DAIFMT_DSP_B:
+               sscr0 |= SSCR0_MOD | SSCR0_PSP;
+               sscr1 |= SSCR1_TRAIL | SSCR1_RWOT;
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       sspsp |= SSPSP_SFRMP;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       ssp_write_reg(ssp, SSCR0, sscr0);
+       ssp_write_reg(ssp, SSCR1, sscr1);
+       ssp_write_reg(ssp, SSPSP, sspsp);
+
+       dump_registers(ssp);
+
+       /* Since we are configuring the timings for the format by hand
+        * we have to defer some things until hw_params() where we
+        * know parameters like the sample size.
+        */
+       priv->dai_fmt = fmt;
+
+       return 0;
+}
+
+/*
+ * Set the SSP audio DMA parameters and sample size.
+ * Can be called multiple times by oss emulation.
+ */
+static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       struct ssp_priv *priv = cpu_dai->private_data;
+       struct ssp_device *ssp = priv->dev.ssp;
+       int dma = 0, chn = params_channels(params);
+       u32 sscr0;
+       u32 sspsp;
+       int width = snd_pcm_format_physical_width(params_format(params));
+
+       /* select correct DMA params */
+       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+               dma = 1; /* capture DMA offset is 1,3 */
+       if (chn == 2)
+               dma += 2; /* stereo DMA offset is 2, mono is 0 */
+       cpu_dai->dma_data = ssp_dma_params[cpu_dai->id][dma];
+
+       dev_dbg(&ssp->pdev->dev, "pxa_ssp_hw_params: dma %d\n", dma);
+
+       /* we can only change the settings if the port is not in use */
+       if (ssp_read_reg(ssp, SSCR0) & SSCR0_SSE)
+               return 0;
+
+       /* clear selected SSP bits */
+       sscr0 = ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS);
+       ssp_write_reg(ssp, SSCR0, sscr0);
+
+       /* bit size */
+       sscr0 = ssp_read_reg(ssp, SSCR0);
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+#ifdef CONFIG_PXA3xx
+               if (cpu_is_pxa3xx())
+                       sscr0 |= SSCR0_FPCKE;
+#endif
+               sscr0 |= SSCR0_DataSize(16);
+               if (params_channels(params) > 1)
+                       sscr0 |= SSCR0_EDSS;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8));
+               /* we must be in network mode (2 slots) for 24 bit stereo */
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16));
+               /* we must be in network mode (2 slots) for 32 bit stereo */
+               break;
+       }
+       ssp_write_reg(ssp, SSCR0, sscr0);
+
+       switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               /* Cleared when the DAI format is set */
+               sspsp = ssp_read_reg(ssp, SSPSP) | SSPSP_SFRMWDTH(width);
+               ssp_write_reg(ssp, SSPSP, sspsp);
+               break;
+       default:
+               break;
+       }
+
+       /* We always use a network mode so we always require TDM slots
+        * - complain loudly and fail if they've not been set up yet.
+        */
+       if (!(ssp_read_reg(ssp, SSTSA) & 0xf)) {
+               dev_err(&ssp->pdev->dev, "No TDM timeslot configured\n");
+               return -EINVAL;
+       }
+
+       dump_registers(ssp);
+
+       return 0;
+}
+
+static int pxa_ssp_trigger(struct snd_pcm_substream *substream, int cmd,
+                          struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       int ret = 0;
+       struct ssp_priv *priv = cpu_dai->private_data;
+       struct ssp_device *ssp = priv->dev.ssp;
+       int val;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_RESUME:
+               ssp_enable(&priv->dev);
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               val = ssp_read_reg(ssp, SSCR1);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       val |= SSCR1_TSRE;
+               else
+                       val |= SSCR1_RSRE;
+               ssp_write_reg(ssp, SSCR1, val);
+               val = ssp_read_reg(ssp, SSSR);
+               ssp_write_reg(ssp, SSSR, val);
+               break;
+       case SNDRV_PCM_TRIGGER_START:
+               val = ssp_read_reg(ssp, SSCR1);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       val |= SSCR1_TSRE;
+               else
+                       val |= SSCR1_RSRE;
+               ssp_write_reg(ssp, SSCR1, val);
+               ssp_enable(&priv->dev);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               val = ssp_read_reg(ssp, SSCR1);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       val &= ~SSCR1_TSRE;
+               else
+                       val &= ~SSCR1_RSRE;
+               ssp_write_reg(ssp, SSCR1, val);
+               break;
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               ssp_disable(&priv->dev);
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               val = ssp_read_reg(ssp, SSCR1);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       val &= ~SSCR1_TSRE;
+               else
+                       val &= ~SSCR1_RSRE;
+               ssp_write_reg(ssp, SSCR1, val);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       dump_registers(ssp);
+
+       return ret;
+}
+
+static int pxa_ssp_probe(struct platform_device *pdev,
+                           struct snd_soc_dai *dai)
+{
+       struct ssp_priv *priv;
+       int ret;
+
+       priv = kzalloc(sizeof(struct ssp_priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->dev.ssp = ssp_request(dai->id, "SoC audio");
+       if (priv->dev.ssp == NULL) {
+               ret = -ENODEV;
+               goto err_priv;
+       }
+
+       dai->private_data = priv;
+
+       return 0;
+
+err_priv:
+       kfree(priv);
+       return ret;
+}
+
+static void pxa_ssp_remove(struct platform_device *pdev,
+                             struct snd_soc_dai *dai)
+{
+       struct ssp_priv *priv = dai->private_data;
+       ssp_free(priv->dev.ssp);
+}
+
+#define PXA_SSP_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+                         SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+                         SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+                         SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+                           SNDRV_PCM_FMTBIT_S24_LE |   \
+                           SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai pxa_ssp_dai[] = {
+       {
+               .name = "pxa2xx-ssp1",
+               .id = 0,
+               .probe = pxa_ssp_probe,
+               .remove = pxa_ssp_remove,
+               .suspend = pxa_ssp_suspend,
+               .resume = pxa_ssp_resume,
+               .playback = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = PXA_SSP_RATES,
+                       .formats = PXA_SSP_FORMATS,
+               },
+               .capture = {
+                        .channels_min = 1,
+                        .channels_max = 2,
+                       .rates = PXA_SSP_RATES,
+                       .formats = PXA_SSP_FORMATS,
+                },
+               .ops = {
+                       .startup = pxa_ssp_startup,
+                       .shutdown = pxa_ssp_shutdown,
+                       .trigger = pxa_ssp_trigger,
+                       .hw_params = pxa_ssp_hw_params,
+                       .set_sysclk = pxa_ssp_set_dai_sysclk,
+                       .set_clkdiv = pxa_ssp_set_dai_clkdiv,
+                       .set_pll = pxa_ssp_set_dai_pll,
+                       .set_fmt = pxa_ssp_set_dai_fmt,
+                       .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
+                       .set_tristate = pxa_ssp_set_dai_tristate,
+               },
+       },
+       {       .name = "pxa2xx-ssp2",
+               .id = 1,
+               .probe = pxa_ssp_probe,
+               .remove = pxa_ssp_remove,
+               .suspend = pxa_ssp_suspend,
+               .resume = pxa_ssp_resume,
+               .playback = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = PXA_SSP_RATES,
+                       .formats = PXA_SSP_FORMATS,
+               },
+               .capture = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = PXA_SSP_RATES,
+                       .formats = PXA_SSP_FORMATS,
+                },
+               .ops = {
+                       .startup = pxa_ssp_startup,
+                       .shutdown = pxa_ssp_shutdown,
+                       .trigger = pxa_ssp_trigger,
+                       .hw_params = pxa_ssp_hw_params,
+                       .set_sysclk = pxa_ssp_set_dai_sysclk,
+                       .set_clkdiv = pxa_ssp_set_dai_clkdiv,
+                       .set_pll = pxa_ssp_set_dai_pll,
+                       .set_fmt = pxa_ssp_set_dai_fmt,
+                       .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
+                       .set_tristate = pxa_ssp_set_dai_tristate,
+               },
+       },
+       {
+               .name = "pxa2xx-ssp3",
+               .id = 2,
+               .probe = pxa_ssp_probe,
+               .remove = pxa_ssp_remove,
+               .suspend = pxa_ssp_suspend,
+               .resume = pxa_ssp_resume,
+               .playback = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = PXA_SSP_RATES,
+                       .formats = PXA_SSP_FORMATS,
+               },
+               .capture = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = PXA_SSP_RATES,
+                       .formats = PXA_SSP_FORMATS,
+                },
+               .ops = {
+                       .startup = pxa_ssp_startup,
+                       .shutdown = pxa_ssp_shutdown,
+                       .trigger = pxa_ssp_trigger,
+                       .hw_params = pxa_ssp_hw_params,
+                       .set_sysclk = pxa_ssp_set_dai_sysclk,
+                       .set_clkdiv = pxa_ssp_set_dai_clkdiv,
+                       .set_pll = pxa_ssp_set_dai_pll,
+                       .set_fmt = pxa_ssp_set_dai_fmt,
+                       .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
+                       .set_tristate = pxa_ssp_set_dai_tristate,
+               },
+       },
+       {
+               .name = "pxa2xx-ssp4",
+               .id = 3,
+               .probe = pxa_ssp_probe,
+               .remove = pxa_ssp_remove,
+               .suspend = pxa_ssp_suspend,
+               .resume = pxa_ssp_resume,
+               .playback = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = PXA_SSP_RATES,
+                       .formats = PXA_SSP_FORMATS,
+               },
+               .capture = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = PXA_SSP_RATES,
+                       .formats = PXA_SSP_FORMATS,
+                },
+               .ops = {
+                       .startup = pxa_ssp_startup,
+                       .shutdown = pxa_ssp_shutdown,
+                       .trigger = pxa_ssp_trigger,
+                       .hw_params = pxa_ssp_hw_params,
+                       .set_sysclk = pxa_ssp_set_dai_sysclk,
+                       .set_clkdiv = pxa_ssp_set_dai_clkdiv,
+                       .set_pll = pxa_ssp_set_dai_pll,
+                       .set_fmt = pxa_ssp_set_dai_fmt,
+                       .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
+                       .set_tristate = pxa_ssp_set_dai_tristate,
+               },
+       },
+};
+EXPORT_SYMBOL_GPL(pxa_ssp_dai);
+
+static int __init pxa_ssp_init(void)
+{
+       return snd_soc_register_dais(pxa_ssp_dai, ARRAY_SIZE(pxa_ssp_dai));
+}
+module_init(pxa_ssp_init);
+
+static void __exit pxa_ssp_exit(void)
+{
+       snd_soc_unregister_dais(pxa_ssp_dai, ARRAY_SIZE(pxa_ssp_dai));
+}
+module_exit(pxa_ssp_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("PXA SSP/PCM SoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/pxa-ssp.h b/sound/soc/pxa/pxa-ssp.h
new file mode 100644 (file)
index 0000000..91deadd
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * ASoC PXA SSP port support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _PXA_SSP_H
+#define _PXA_SSP_H
+
+/* pxa DAI SSP IDs */
+#define PXA_DAI_SSP1                   0
+#define PXA_DAI_SSP2                   1
+#define PXA_DAI_SSP3                   2
+#define PXA_DAI_SSP4                   3
+
+/* SSP clock sources */
+#define PXA_SSP_CLK_PLL        0
+#define PXA_SSP_CLK_EXT        1
+#define PXA_SSP_CLK_NET        2
+#define PXA_SSP_CLK_AUDIO      3
+#define PXA_SSP_CLK_NET_PLL    4
+
+/* SSP audio dividers */
+#define PXA_SSP_AUDIO_DIV_ACDS         0
+#define PXA_SSP_AUDIO_DIV_SCDB         1
+#define PXA_SSP_DIV_SCR                                2
+
+/* SSP ACDS audio dividers values */
+#define PXA_SSP_CLK_AUDIO_DIV_1                0
+#define PXA_SSP_CLK_AUDIO_DIV_2                1
+#define PXA_SSP_CLK_AUDIO_DIV_4                2
+#define PXA_SSP_CLK_AUDIO_DIV_8                3
+#define PXA_SSP_CLK_AUDIO_DIV_16       4
+#define PXA_SSP_CLK_AUDIO_DIV_32       5
+
+/* SSP divider bypass */
+#define PXA_SSP_CLK_SCDB_4             0
+#define PXA_SSP_CLK_SCDB_1             1
+#define PXA_SSP_CLK_SCDB_8             2
+
+#define PXA_SSP_PLL_OUT  0
+
+extern struct snd_soc_dai pxa_ssp_dai[4];
+
+#endif
index a7a3a9c..780db67 100644 (file)
@@ -87,14 +87,12 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = {
 };
 
 #ifdef CONFIG_PM
-static int pxa2xx_ac97_suspend(struct platform_device *pdev,
-       struct snd_soc_dai *dai)
+static int pxa2xx_ac97_suspend(struct snd_soc_dai *dai)
 {
        return pxa2xx_ac97_hw_suspend();
 }
 
-static int pxa2xx_ac97_resume(struct platform_device *pdev,
-       struct snd_soc_dai *dai)
+static int pxa2xx_ac97_resume(struct snd_soc_dai *dai)
 {
        return pxa2xx_ac97_hw_resume();
 }
@@ -117,7 +115,8 @@ static void pxa2xx_ac97_remove(struct platform_device *pdev,
 }
 
 static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
-                               struct snd_pcm_hw_params *params)
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -131,7 +130,8 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
 }
 
 static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
+                                    struct snd_pcm_hw_params *params,
+                                    struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -145,7 +145,8 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
 }
 
 static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
+                                    struct snd_pcm_hw_params *params,
+                                    struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -170,7 +171,7 @@ struct snd_soc_dai pxa_ac97_dai[] = {
 {
        .name = "pxa2xx-ac97",
        .id = 0,
-       .type = SND_SOC_DAI_AC97,
+       .ac97_control = 1,
        .probe = pxa2xx_ac97_probe,
        .remove = pxa2xx_ac97_remove,
        .suspend = pxa2xx_ac97_suspend,
@@ -193,7 +194,7 @@ struct snd_soc_dai pxa_ac97_dai[] = {
 {
        .name = "pxa2xx-ac97-aux",
        .id = 1,
-       .type = SND_SOC_DAI_AC97,
+       .ac97_control = 1,
        .playback = {
                .stream_name = "AC97 Aux Playback",
                .channels_min = 1,
@@ -212,7 +213,7 @@ struct snd_soc_dai pxa_ac97_dai[] = {
 {
        .name = "pxa2xx-ac97-mic",
        .id = 2,
-       .type = SND_SOC_DAI_AC97,
+       .ac97_control = 1,
        .capture = {
                .stream_name = "AC97 Mic Capture",
                .channels_min = 1,
@@ -227,6 +228,18 @@ struct snd_soc_dai pxa_ac97_dai[] = {
 EXPORT_SYMBOL_GPL(pxa_ac97_dai);
 EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
+static int __init pxa_ac97_init(void)
+{
+       return snd_soc_register_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai));
+}
+module_init(pxa_ac97_init);
+
+static void __exit pxa_ac97_exit(void)
+{
+       snd_soc_unregister_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai));
+}
+module_exit(pxa_ac97_exit);
+
 MODULE_AUTHOR("Nicolas Pitre");
 MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
 MODULE_LICENSE("GPL");
index e758034..517991f 100644 (file)
@@ -121,7 +121,8 @@ static struct pxa2xx_gpio gpio_bus[] = {
        },
 };
 
-static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream)
+static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream,
+                             struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -187,7 +188,8 @@ static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
 }
 
 static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
-                               struct snd_pcm_hw_params *params)
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -248,7 +250,8 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+                             struct snd_soc_dai *dai)
 {
        int ret = 0;
 
@@ -269,7 +272,8 @@ static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
        return ret;
 }
 
-static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream)
+static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
 {
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                SACR1 |= SACR1_DRPL;
@@ -289,8 +293,7 @@ static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream)
 }
 
 #ifdef CONFIG_PM
-static int pxa2xx_i2s_suspend(struct platform_device *dev,
-       struct snd_soc_dai *dai)
+static int pxa2xx_i2s_suspend(struct snd_soc_dai *dai)
 {
        if (!dai->active)
                return 0;
@@ -307,8 +310,7 @@ static int pxa2xx_i2s_suspend(struct platform_device *dev,
        return 0;
 }
 
-static int pxa2xx_i2s_resume(struct platform_device *pdev,
-       struct snd_soc_dai *dai)
+static int pxa2xx_i2s_resume(struct snd_soc_dai *dai)
 {
        if (!dai->active)
                return 0;
@@ -336,7 +338,6 @@ static int pxa2xx_i2s_resume(struct platform_device *pdev,
 struct snd_soc_dai pxa_i2s_dai = {
        .name = "pxa2xx-i2s",
        .id = 0,
-       .type = SND_SOC_DAI_I2S,
        .suspend = pxa2xx_i2s_suspend,
        .resume = pxa2xx_i2s_resume,
        .playback = {
@@ -353,8 +354,7 @@ struct snd_soc_dai pxa_i2s_dai = {
                .startup = pxa2xx_i2s_startup,
                .shutdown = pxa2xx_i2s_shutdown,
                .trigger = pxa2xx_i2s_trigger,
-               .hw_params = pxa2xx_i2s_hw_params,},
-       .dai_ops = {
+               .hw_params = pxa2xx_i2s_hw_params,
                .set_fmt = pxa2xx_i2s_set_dai_fmt,
                .set_sysclk = pxa2xx_i2s_set_dai_sysclk,
        },
@@ -364,12 +364,23 @@ EXPORT_SYMBOL_GPL(pxa_i2s_dai);
 
 static int pxa2xx_i2s_probe(struct platform_device *dev)
 {
+       int ret;
+
        clk_i2s = clk_get(&dev->dev, "I2SCLK");
-       return IS_ERR(clk_i2s) ? PTR_ERR(clk_i2s) : 0;
+       if (IS_ERR(clk_i2s))
+               return PTR_ERR(clk_i2s);
+
+       pxa_i2s_dai.dev = &dev->dev;
+       ret = snd_soc_register_dai(&pxa_i2s_dai);
+       if (ret != 0)
+               clk_put(clk_i2s);
+
+       return ret;
 }
 
 static int __devexit pxa2xx_i2s_remove(struct platform_device *dev)
 {
+       snd_soc_unregister_dai(&pxa_i2s_dai);
        clk_put(clk_i2s);
        clk_i2s = ERR_PTR(-ENOENT);
        return 0;
index afcd892..c670d08 100644 (file)
@@ -69,7 +69,7 @@ static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
        return 0;
 }
 
-struct snd_pcm_ops pxa2xx_pcm_ops = {
+static struct snd_pcm_ops pxa2xx_pcm_ops = {
        .open           = __pxa2xx_pcm_open,
        .close          = __pxa2xx_pcm_close,
        .ioctl          = snd_pcm_lib_ioctl,
@@ -118,6 +118,18 @@ struct snd_soc_platform pxa2xx_soc_platform = {
 };
 EXPORT_SYMBOL_GPL(pxa2xx_soc_platform);
 
+static int __init pxa2xx_soc_platform_init(void)
+{
+       return snd_soc_register_platform(&pxa2xx_soc_platform);
+}
+module_init(pxa2xx_soc_platform_init);
+
+static void __exit pxa2xx_soc_platform_exit(void)
+{
+       snd_soc_unregister_platform(&pxa2xx_soc_platform);
+}
+module_exit(pxa2xx_soc_platform_exit);
+
 MODULE_AUTHOR("Nicolas Pitre");
 MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module");
 MODULE_LICENSE("GPL");
index d307b67..a3b9e6b 100644 (file)
@@ -319,8 +319,9 @@ static struct snd_soc_dai_link spitz_dai = {
 };
 
 /* spitz audio machine driver */
-static struct snd_soc_machine snd_soc_machine_spitz = {
+static struct snd_soc_card snd_soc_spitz = {
        .name = "Spitz",
+       .platform = &pxa2xx_soc_platform,
        .dai_link = &spitz_dai,
        .num_links = 1,
 };
@@ -333,8 +334,7 @@ static struct wm8750_setup_data spitz_wm8750_setup = {
 
 /* spitz audio subsystem */
 static struct snd_soc_device spitz_snd_devdata = {
-       .machine = &snd_soc_machine_spitz,
-       .platform = &pxa2xx_soc_platform,
+       .card = &snd_soc_spitz,
        .codec_dev = &soc_codec_dev_wm8750,
        .codec_data = &spitz_wm8750_setup,
 };
index afefe41..c77194f 100644 (file)
@@ -38,7 +38,7 @@
 #include "pxa2xx-pcm.h"
 #include "pxa2xx-ac97.h"
 
-static struct snd_soc_machine tosa;
+static struct snd_soc_card tosa;
 
 #define TOSA_HP        0
 #define TOSA_MIC_INT   1
@@ -230,15 +230,37 @@ static struct snd_soc_dai_link tosa_dai[] = {
 },
 };
 
-static struct snd_soc_machine tosa = {
+static int tosa_probe(struct platform_device *dev)
+{
+       int ret;
+
+       ret = gpio_request(TOSA_GPIO_L_MUTE, "Headphone Jack");
+       if (ret)
+               return ret;
+       ret = gpio_direction_output(TOSA_GPIO_L_MUTE, 0);
+       if (ret)
+               gpio_free(TOSA_GPIO_L_MUTE);
+
+       return ret;
+}
+
+static int tosa_remove(struct platform_device *dev)
+{
+       gpio_free(TOSA_GPIO_L_MUTE);
+       return 0;
+}
+
+static struct snd_soc_card tosa = {
        .name = "Tosa",
+       .platform = &pxa2xx_soc_platform,
        .dai_link = tosa_dai,
        .num_links = ARRAY_SIZE(tosa_dai),
+       .probe = tosa_probe,
+       .remove = tosa_remove,
 };
 
 static struct snd_soc_device tosa_snd_devdata = {
-       .machine = &tosa,
-       .platform = &pxa2xx_soc_platform,
+       .card = &tosa,
        .codec_dev = &soc_codec_dev_wm9712,
 };
 
@@ -251,11 +273,6 @@ static int __init tosa_init(void)
        if (!machine_is_tosa())
                return -ENODEV;
 
-       ret = gpio_request(TOSA_GPIO_L_MUTE, "Headphone Jack");
-       if (ret)
-               return ret;
-       gpio_direction_output(TOSA_GPIO_L_MUTE, 0);
-
        tosa_snd_device = platform_device_alloc("soc-audio", -1);
        if (!tosa_snd_device) {
                ret = -ENOMEM;
@@ -272,15 +289,12 @@ static int __init tosa_init(void)
        platform_device_put(tosa_snd_device);
 
 err_alloc:
-       gpio_free(TOSA_GPIO_L_MUTE);
-
        return ret;
 }
 
 static void __exit tosa_exit(void)
 {
        platform_device_unregister(tosa_snd_device);
-       gpio_free(TOSA_GPIO_L_MUTE);
 }
 
 module_init(tosa_init);
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c
new file mode 100644 (file)
index 0000000..f8e9ecd
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * zylonite.c  --  SoC audio for Zylonite
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/wm9713.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+#include "pxa-ssp.h"
+
+static struct snd_soc_card zylonite;
+
+static const struct snd_soc_dapm_widget zylonite_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_MIC("Headset Microphone", NULL),
+       SND_SOC_DAPM_MIC("Handset Microphone", NULL),
+       SND_SOC_DAPM_SPK("Multiactor", NULL),
+       SND_SOC_DAPM_SPK("Headset Earpiece", NULL),
+};
+
+/* Currently supported audio map */
+static const struct snd_soc_dapm_route audio_map[] = {
+
+       /* Headphone output connected to HPL/HPR */
+       { "Headphone", NULL,  "HPL" },
+       { "Headphone", NULL,  "HPR" },
+
+       /* On-board earpiece */
+       { "Headset Earpiece", NULL, "OUT3" },
+
+       /* Headphone mic */
+       { "MIC2A", NULL, "Mic Bias" },
+       { "Mic Bias", NULL, "Headset Microphone" },
+
+       /* On-board mic */
+       { "MIC1", NULL, "Mic Bias" },
+       { "Mic Bias", NULL, "Handset Microphone" },
+
+       /* Multiactor differentially connected over SPKL/SPKR */
+       { "Multiactor", NULL, "SPKL" },
+       { "Multiactor", NULL, "SPKR" },
+};
+
+static int zylonite_wm9713_init(struct snd_soc_codec *codec)
+{
+       /* Currently we only support use of the AC97 clock here.  If
+        * CLK_POUT is selected by SW15 then the clock API will need
+        * to be used to request and enable it here.
+        */
+
+       snd_soc_dapm_new_controls(codec, zylonite_dapm_widgets,
+                                 ARRAY_SIZE(zylonite_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       /* Static setup for now */
+       snd_soc_dapm_enable_pin(codec, "Headphone");
+       snd_soc_dapm_enable_pin(codec, "Headset Earpiece");
+
+       snd_soc_dapm_sync(codec);
+       return 0;
+}
+
+static int zylonite_voice_hw_params(struct snd_pcm_substream *substream,
+                                   struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       unsigned int pll_out = 0;
+       unsigned int acds = 0;
+       unsigned int wm9713_div = 0;
+       int ret = 0;
+
+       switch (params_rate(params)) {
+       case 8000:
+               wm9713_div = 12;
+               pll_out = 2048000;
+               break;
+       case 16000:
+               wm9713_div = 6;
+               pll_out = 4096000;
+               break;
+       case 48000:
+       default:
+               wm9713_div = 2;
+               pll_out = 12288000;
+               acds = 1;
+               break;
+       }
+
+       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_tdm_slot(cpu_dai,
+                                      params_channels(params),
+                                      params_channels(params));
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, pll_out);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_AUDIO_DIV_ACDS, acds);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1);
+       if (ret < 0)
+               return ret;
+
+       /* Note that if the PLL is in use the WM9713_PCMCLK_PLL_DIV needs
+        * to be set instead.
+        */
+       ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_DIV,
+                                    WM9713_PCMDIV(wm9713_div));
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static struct snd_soc_ops zylonite_voice_ops = {
+       .hw_params = zylonite_voice_hw_params,
+};
+
+static struct snd_soc_dai_link zylonite_dai[] = {
+{
+       .name = "AC97",
+       .stream_name = "AC97 HiFi",
+       .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
+       .codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
+       .init = zylonite_wm9713_init,
+},
+{
+       .name = "AC97 Aux",
+       .stream_name = "AC97 Aux",
+       .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
+       .codec_dai = &wm9713_dai[WM9713_DAI_AC97_AUX],
+},
+{
+       .name = "WM9713 Voice",
+       .stream_name = "WM9713 Voice",
+       .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP3],
+       .codec_dai = &wm9713_dai[WM9713_DAI_PCM_VOICE],
+       .ops = &zylonite_voice_ops,
+},
+};
+
+static struct snd_soc_card zylonite = {
+       .name = "Zylonite",
+       .platform = &pxa2xx_soc_platform,
+       .dai_link = zylonite_dai,
+       .num_links = ARRAY_SIZE(zylonite_dai),
+};
+
+static struct snd_soc_device zylonite_snd_ac97_devdata = {
+       .card = &zylonite,
+       .codec_dev = &soc_codec_dev_wm9713,
+};
+
+static struct platform_device *zylonite_snd_ac97_device;
+
+static int __init zylonite_init(void)
+{
+       int ret;
+
+       zylonite_snd_ac97_device = platform_device_alloc("soc-audio", -1);
+       if (!zylonite_snd_ac97_device)
+               return -ENOMEM;
+
+       platform_set_drvdata(zylonite_snd_ac97_device,
+                            &zylonite_snd_ac97_devdata);
+       zylonite_snd_ac97_devdata.dev = &zylonite_snd_ac97_device->dev;
+
+       ret = platform_device_add(zylonite_snd_ac97_device);
+       if (ret != 0)
+               platform_device_put(zylonite_snd_ac97_device);
+
+       return ret;
+}
+
+static void __exit zylonite_exit(void)
+{
+       platform_device_unregister(zylonite_snd_ac97_device);
+}
+
+module_init(zylonite_init);
+module_exit(zylonite_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("ALSA SoC WM9713 Zylonite");
+MODULE_LICENSE("GPL");
index b9f2353..fcd03ac 100644 (file)
@@ -44,3 +44,8 @@ config SND_S3C24XX_SOC_LN2440SBC_ALC650
          Say Y if you want to add support for SoC audio on ln2440sbc
          with the ALC650.
 
+config SND_S3C24XX_SOC_S3C24XX_UDA134X
+       tristate "SoC I2S Audio support UDA134X wired to a S3C24XX"
+               depends on SND_S3C24XX_SOC
+               select SND_S3C24XX_SOC_I2S
+               select SND_SOC_UDA134X
index 0aa5fb0..96b3f3f 100644 (file)
@@ -13,7 +13,9 @@ obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
 snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
 snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
 snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
+snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
 
 obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
 obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
+obj-$(CONFIG_SND_S3C24XX_SOC_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o
index 4eab2c1..12c7148 100644 (file)
@@ -27,7 +27,7 @@
 #include "s3c24xx-pcm.h"
 #include "s3c24xx-ac97.h"
 
-static struct snd_soc_machine ln2440sbc;
+static struct snd_soc_card ln2440sbc;
 
 static struct snd_soc_dai_link ln2440sbc_dai[] = {
 {
@@ -38,15 +38,15 @@ static struct snd_soc_dai_link ln2440sbc_dai[] = {
 },
 };
 
-static struct snd_soc_machine ln2440sbc = {
+static struct snd_soc_card ln2440sbc = {
        .name = "LN2440SBC",
+       .platform = &s3c24xx_soc_platform,
        .dai_link = ln2440sbc_dai,
        .num_links = ARRAY_SIZE(ln2440sbc_dai),
 };
 
 static struct snd_soc_device ln2440sbc_snd_ac97_devdata = {
-       .machine = &ln2440sbc,
-       .platform = &s3c24xx_soc_platform,
+       .card = &ln2440sbc,
        .codec_dev = &soc_codec_dev_ac97,
 };
 
index 87ddfef..45bb12e 100644 (file)
@@ -59,7 +59,7 @@
 #define NEO_CAPTURE_HEADSET            7
 #define NEO_CAPTURE_BLUETOOTH          8
 
-static struct snd_soc_machine neo1973;
+static struct snd_soc_card neo1973;
 static struct i2c_client *i2c;
 
 static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
@@ -548,7 +548,6 @@ static int neo1973_wm8753_init(struct snd_soc_codec *codec)
 static struct snd_soc_dai bt_dai = {
        .name = "Bluetooth",
        .id = 0,
-       .type = SND_SOC_DAI_PCM,
        .playback = {
                .channels_min = 1,
                .channels_max = 1,
@@ -579,8 +578,9 @@ static struct snd_soc_dai_link neo1973_dai[] = {
 },
 };
 
-static struct snd_soc_machine neo1973 = {
+static struct snd_soc_card neo1973 = {
        .name = "neo1973",
+       .platform = &s3c24xx_soc_platform,
        .dai_link = neo1973_dai,
        .num_links = ARRAY_SIZE(neo1973_dai),
 };
@@ -591,8 +591,7 @@ static struct wm8753_setup_data neo1973_wm8753_setup = {
 };
 
 static struct snd_soc_device neo1973_snd_devdata = {
-       .machine = &neo1973,
-       .platform = &s3c24xx_soc_platform,
+       .card = &neo1973,
        .codec_dev = &soc_codec_dev_wm8753,
        .codec_data = &neo1973_wm8753_setup,
 };
index ded7d99..f3fc0ab 100644 (file)
@@ -343,7 +343,8 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
 }
 
 static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
-                                struct snd_pcm_hw_params *params)
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        u32 iismod;
@@ -373,7 +374,8 @@ static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+                              struct snd_soc_dai *dai)
 {
        int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
        unsigned long irqs;
@@ -647,8 +649,7 @@ static int s3c2412_i2s_probe(struct platform_device *pdev,
 }
 
 #ifdef CONFIG_PM
-static int s3c2412_i2s_suspend(struct platform_device *dev,
-                             struct snd_soc_dai *dai)
+static int s3c2412_i2s_suspend(struct snd_soc_dai *dai)
 {
        struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
        u32 iismod;
@@ -663,25 +664,24 @@ static int s3c2412_i2s_suspend(struct platform_device *dev,
                iismod = readl(i2s->regs + S3C2412_IISMOD);
 
                if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
-                       dev_warn(&dev->dev, "%s: RXDMA active?\n", __func__);
+                       pr_warning("%s: RXDMA active?\n", __func__);
 
                if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
-                       dev_warn(&dev->dev, "%s: TXDMA active?\n", __func__);
+                       pr_warning("%s: TXDMA active?\n", __func__);
 
                if (iismod & S3C2412_IISCON_IIS_ACTIVE)
-                       dev_warn(&dev->dev, "%s: IIS active\n", __func__);
+                       pr_warning("%s: IIS active\n", __func__);
        }
 
        return 0;
 }
 
-static int s3c2412_i2s_resume(struct platform_device *pdev,
-                             struct snd_soc_dai *dai)
+static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
 {
        struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
 
-       dev_info(&pdev->dev, "dai_active %d, IISMOD %08x, IISCON %08x\n",
-                dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
+       pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n",
+               dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
 
        if (dai->active) {
                writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
@@ -711,7 +711,6 @@ static int s3c2412_i2s_resume(struct platform_device *pdev,
 struct snd_soc_dai s3c2412_i2s_dai = {
        .name   = "s3c2412-i2s",
        .id     = 0,
-       .type   = SND_SOC_DAI_I2S,
        .probe  = s3c2412_i2s_probe,
        .suspend = s3c2412_i2s_suspend,
        .resume = s3c2412_i2s_resume,
@@ -730,8 +729,6 @@ struct snd_soc_dai s3c2412_i2s_dai = {
        .ops = {
                .trigger        = s3c2412_i2s_trigger,
                .hw_params      = s3c2412_i2s_hw_params,
-       },
-       .dai_ops = {
                .set_fmt        = s3c2412_i2s_set_fmt,
                .set_clkdiv     = s3c2412_i2s_set_clkdiv,
                .set_sysclk     = s3c2412_i2s_set_sysclk,
@@ -739,6 +736,19 @@ struct snd_soc_dai s3c2412_i2s_dai = {
 };
 EXPORT_SYMBOL_GPL(s3c2412_i2s_dai);
 
+static int __init s3c2412_i2s_init(void)
+{
+       return snd_soc_register_dai(&s3c2412_i2s_dai);
+}
+module_init(s3c2412_i2s_init);
+
+static void __exit s3c2412_i2s_exit(void)
+{
+       snd_soc_unregister_dai(&s3c2412_i2s_dai);
+}
+module_exit(s3c2412_i2s_exit);
+
+
 /* Module information */
 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
 MODULE_DESCRIPTION("S3C2412 I2S SoC Interface");
index 19c5c3c..1bfce40 100644 (file)
@@ -271,7 +271,8 @@ static void s3c2443_ac97_remove(struct platform_device *pdev,
 }
 
 static int s3c2443_ac97_hw_params(struct snd_pcm_substream *substream,
-                               struct snd_pcm_hw_params *params)
+                                 struct snd_pcm_hw_params *params,
+                                 struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -284,7 +285,8 @@ static int s3c2443_ac97_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd)
+static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
+                               struct snd_soc_dai *dai)
 {
        u32 ac_glbctrl;
 
@@ -313,7 +315,8 @@ static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd)
 }
 
 static int s3c2443_ac97_hw_mic_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
+                                     struct snd_pcm_hw_params *params,
+                                     struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -327,7 +330,7 @@ static int s3c2443_ac97_hw_mic_params(struct snd_pcm_substream *substream,
 }
 
 static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
-       int cmd)
+                                   int cmd, struct snd_soc_dai *dai)
 {
        u32 ac_glbctrl;
 
@@ -356,7 +359,7 @@ struct snd_soc_dai s3c2443_ac97_dai[] = {
 {
        .name = "s3c2443-ac97",
        .id = 0,
-       .type = SND_SOC_DAI_AC97,
+       .ac97_control = 1,
        .probe = s3c2443_ac97_probe,
        .remove = s3c2443_ac97_remove,
        .playback = {
@@ -378,7 +381,7 @@ struct snd_soc_dai s3c2443_ac97_dai[] = {
 {
        .name = "pxa2xx-ac97-mic",
        .id = 1,
-       .type = SND_SOC_DAI_AC97,
+       .ac97_control = 1,
        .capture = {
                .stream_name = "AC97 Mic Capture",
                .channels_min = 1,
@@ -393,6 +396,21 @@ struct snd_soc_dai s3c2443_ac97_dai[] = {
 EXPORT_SYMBOL_GPL(s3c2443_ac97_dai);
 EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
+static int __init s3c2443_ac97_init(void)
+{
+       return snd_soc_register_dais(s3c2443_ac97_dai,
+                                    ARRAY_SIZE(s3c2443_ac97_dai));
+}
+module_init(s3c2443_ac97_init);
+
+static void __exit s3c2443_ac97_exit(void)
+{
+       snd_soc_unregister_dais(s3c2443_ac97_dai,
+                               ARRAY_SIZE(s3c2443_ac97_dai));
+}
+module_exit(s3c2443_ac97_exit);
+
+
 MODULE_AUTHOR("Graeme Gregory");
 MODULE_DESCRIPTION("AC97 driver for the Samsung s3c2443 chip");
 MODULE_LICENSE("GPL");
index ba4476b..6f4d439 100644 (file)
@@ -243,7 +243,8 @@ static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
 }
 
 static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
-                               struct snd_pcm_hw_params *params)
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        u32 iismod;
@@ -261,10 +262,17 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
 
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S8:
+               iismod &= ~S3C2410_IISMOD_16BIT;
+               ((struct s3c24xx_pcm_dma_params *)
+                 rtd->dai->cpu_dai->dma_data)->dma_size = 1;
                break;
        case SNDRV_PCM_FORMAT_S16_LE:
                iismod |= S3C2410_IISMOD_16BIT;
+               ((struct s3c24xx_pcm_dma_params *)
+                 rtd->dai->cpu_dai->dma_data)->dma_size = 2;
                break;
+       default:
+               return -EINVAL;
        }
 
        writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
@@ -272,7 +280,8 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+                              struct snd_soc_dai *dai)
 {
        int ret = 0;
 
@@ -410,8 +419,7 @@ static int s3c24xx_i2s_probe(struct platform_device *pdev,
 }
 
 #ifdef CONFIG_PM
-static int s3c24xx_i2s_suspend(struct platform_device *pdev,
-               struct snd_soc_dai *cpu_dai)
+static int s3c24xx_i2s_suspend(struct snd_soc_dai *cpu_dai)
 {
        DBG("Entered %s\n", __func__);
 
@@ -425,8 +433,7 @@ static int s3c24xx_i2s_suspend(struct platform_device *pdev,
        return 0;
 }
 
-static int s3c24xx_i2s_resume(struct platform_device *pdev,
-               struct snd_soc_dai *cpu_dai)
+static int s3c24xx_i2s_resume(struct snd_soc_dai *cpu_dai)
 {
        DBG("Entered %s\n", __func__);
        clk_enable(s3c24xx_i2s.iis_clk);
@@ -452,7 +459,6 @@ static int s3c24xx_i2s_resume(struct platform_device *pdev,
 struct snd_soc_dai s3c24xx_i2s_dai = {
        .name = "s3c24xx-i2s",
        .id = 0,
-       .type = SND_SOC_DAI_I2S,
        .probe = s3c24xx_i2s_probe,
        .suspend = s3c24xx_i2s_suspend,
        .resume = s3c24xx_i2s_resume,
@@ -468,8 +474,7 @@ struct snd_soc_dai s3c24xx_i2s_dai = {
                .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
        .ops = {
                .trigger = s3c24xx_i2s_trigger,
-               .hw_params = s3c24xx_i2s_hw_params,},
-       .dai_ops = {
+               .hw_params = s3c24xx_i2s_hw_params,
                .set_fmt = s3c24xx_i2s_set_fmt,
                .set_clkdiv = s3c24xx_i2s_set_clkdiv,
                .set_sysclk = s3c24xx_i2s_set_sysclk,
@@ -477,6 +482,18 @@ struct snd_soc_dai s3c24xx_i2s_dai = {
 };
 EXPORT_SYMBOL_GPL(s3c24xx_i2s_dai);
 
+static int __init s3c24xx_i2s_init(void)
+{
+       return snd_soc_register_dai(&s3c24xx_i2s_dai);
+}
+module_init(s3c24xx_i2s_init);
+
+static void __exit s3c24xx_i2s_exit(void)
+{
+       snd_soc_unregister_dai(&s3c24xx_i2s_dai);
+}
+module_exit(s3c24xx_i2s_exit);
+
 /* Module information */
 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
 MODULE_DESCRIPTION("s3c24xx I2S SoC Interface");
index e13e614..7c64d31 100644 (file)
@@ -465,6 +465,18 @@ struct snd_soc_platform s3c24xx_soc_platform = {
 };
 EXPORT_SYMBOL_GPL(s3c24xx_soc_platform);
 
+static int __init s3c24xx_soc_platform_init(void)
+{
+       return snd_soc_register_platform(&s3c24xx_soc_platform);
+}
+module_init(s3c24xx_soc_platform_init);
+
+static void __exit s3c24xx_soc_platform_exit(void)
+{
+       snd_soc_unregister_platform(&s3c24xx_soc_platform);
+}
+module_exit(s3c24xx_soc_platform_exit);
+
 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
 MODULE_DESCRIPTION("Samsung S3C24XX PCM DMA module");
 MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c24xx_uda134x.c b/sound/soc/s3c24xx/s3c24xx_uda134x.c
new file mode 100644 (file)
index 0000000..a0a4d18
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * Modifications by Christian Pellegrin <chripell@evolware.org>
+ *
+ * s3c24xx_uda134x.c  --  S3C24XX_UDA134X ALSA SoC Audio board driver
+ *
+ * Copyright 2007 Dension Audio Systems Ltd.
+ * Author: Zoltan Devai
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/s3c24xx_uda134x.h>
+#include <sound/uda134x.h>
+
+#include <asm/plat-s3c24xx/regs-iis.h>
+
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-i2s.h"
+#include "../codecs/uda134x.h"
+
+
+/* #define ENFORCE_RATES 1 */
+/*
+  Unfortunately the S3C24XX in master mode has a limited capacity of
+  generating the clock for the codec. If you define this only rates
+  that are really available will be enforced. But be careful, most
+  user level application just want the usual sampling frequencies (8,
+  11.025, 22.050, 44.1 kHz) and anyway resampling is a costly
+  operation for embedded systems. So if you aren't very lucky or your
+  hardware engineer wasn't very forward-looking it's better to leave
+  this undefined. If you do so an approximate value for the requested
+  sampling rate in the range -/+ 5% will be chosen. If this in not
+  possible an error will be returned.
+*/
+
+static struct clk *xtal;
+static struct clk *pclk;
+/* this is need because we don't have a place where to keep the
+ * pointers to the clocks in each substream. We get the clocks only
+ * when we are actually using them so we don't block stuff like
+ * frequency change or oscillator power-off */
+static int clk_users;
+static DEFINE_MUTEX(clk_lock);
+
+static unsigned int rates[33 * 2];
+#ifdef ENFORCE_RATES
+static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+       .count  = ARRAY_SIZE(rates),
+       .list   = rates,
+       .mask   = 0,
+};
+#endif
+
+static struct platform_device *s3c24xx_uda134x_snd_device;
+
+static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
+{
+       int ret = 0;
+#ifdef ENFORCE_RATES
+       struct snd_pcm_runtime *runtime = substream->runtime;;
+#endif
+
+       mutex_lock(&clk_lock);
+       pr_debug("%s %d\n", __func__, clk_users);
+       if (clk_users == 0) {
+               xtal = clk_get(&s3c24xx_uda134x_snd_device->dev, "xtal");
+               if (!xtal) {
+                       printk(KERN_ERR "%s cannot get xtal\n", __func__);
+                       ret = -EBUSY;
+               } else {
+                       pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,
+                                      "pclk");
+                       if (!pclk) {
+                               printk(KERN_ERR "%s cannot get pclk\n",
+                                      __func__);
+                               clk_put(xtal);
+                               ret = -EBUSY;
+                       }
+               }
+               if (!ret) {
+                       int i, j;
+
+                       for (i = 0; i < 2; i++) {
+                               int fs = i ? 256 : 384;
+
+                               rates[i*33] = clk_get_rate(xtal) / fs;
+                               for (j = 1; j < 33; j++)
+                                       rates[i*33 + j] = clk_get_rate(pclk) /
+                                               (j * fs);
+                       }
+               }
+       }
+       clk_users += 1;
+       mutex_unlock(&clk_lock);
+       if (!ret) {
+#ifdef ENFORCE_RATES
+               ret = snd_pcm_hw_constraint_list(runtime, 0,
+                                                SNDRV_PCM_HW_PARAM_RATE,
+                                                &hw_constraints_rates);
+               if (ret < 0)
+                       printk(KERN_ERR "%s cannot set constraints\n",
+                              __func__);
+#endif
+       }
+       return ret;
+}
+
+static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
+{
+       mutex_lock(&clk_lock);
+       pr_debug("%s %d\n", __func__, clk_users);
+       clk_users -= 1;
+       if (clk_users == 0) {
+               clk_put(xtal);
+               xtal = NULL;
+               clk_put(pclk);
+               pclk = NULL;
+       }
+       mutex_unlock(&clk_lock);
+}
+
+static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       unsigned int clk = 0;
+       int ret = 0;
+       int clk_source, fs_mode;
+       unsigned long rate = params_rate(params);
+       long err, cerr;
+       unsigned int div;
+       int i, bi;
+
+       err = 999999;
+       bi = 0;
+       for (i = 0; i < 2*33; i++) {
+               cerr = rates[i] - rate;
+               if (cerr < 0)
+                       cerr = -cerr;
+               if (cerr < err) {
+                       err = cerr;
+                       bi = i;
+               }
+       }
+       if (bi / 33 == 1)
+               fs_mode = S3C2410_IISMOD_256FS;
+       else
+               fs_mode = S3C2410_IISMOD_384FS;
+       if (bi % 33 == 0) {
+               clk_source = S3C24XX_CLKSRC_MPLL;
+               div = 1;
+       } else {
+               clk_source = S3C24XX_CLKSRC_PCLK;
+               div = bi % 33;
+       }
+       pr_debug("%s desired rate %lu, %d\n", __func__, rate, bi);
+
+       clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
+       pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__,
+                fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
+                clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
+                div, clk, err);
+
+       if ((err * 100 / rate) > 5) {
+               printk(KERN_ERR "S3C24XX_UDA134X: effective frequency "
+                      "too different from desired (%ld%%)\n",
+                      err * 100 / rate);
+               return -EINVAL;
+       }
+
+       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+                       SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+                       SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
+                       SND_SOC_CLOCK_IN);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
+                       S3C2410_IISMOD_32FS);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
+                       S3C24XX_PRESCALE(div, div));
+       if (ret < 0)
+               return ret;
+
+       /* set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
+                       SND_SOC_CLOCK_OUT);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static struct snd_soc_ops s3c24xx_uda134x_ops = {
+       .startup = s3c24xx_uda134x_startup,
+       .shutdown = s3c24xx_uda134x_shutdown,
+       .hw_params = s3c24xx_uda134x_hw_params,
+};
+
+static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
+       .name = "UDA134X",
+       .stream_name = "UDA134X",
+       .codec_dai = &uda134x_dai,
+       .cpu_dai = &s3c24xx_i2s_dai,
+       .ops = &s3c24xx_uda134x_ops,
+};
+
+static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
+       .name = "S3C24XX_UDA134X",
+       .platform = &s3c24xx_soc_platform,
+       .dai_link = &s3c24xx_uda134x_dai_link,
+       .num_links = 1,
+};
+
+static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
+
+static void setdat(int v)
+{
+       gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
+}
+
+static void setclk(int v)
+{
+       gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
+}
+
+static void setmode(int v)
+{
+       gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
+}
+
+static struct uda134x_platform_data s3c24xx_uda134x = {
+       .l3 = {
+               .setdat = setdat,
+               .setclk = setclk,
+               .setmode = setmode,
+               .data_hold = 1,
+               .data_setup = 1,
+               .clock_high = 1,
+               .mode_hold = 1,
+               .mode = 1,
+               .mode_setup = 1,
+       },
+};
+
+static struct snd_soc_device s3c24xx_uda134x_snd_devdata = {
+       .card = &snd_soc_s3c24xx_uda134x,
+       .codec_dev = &soc_codec_dev_uda134x,
+       .codec_data = &s3c24xx_uda134x,
+};
+
+static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
+{
+       if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
+               printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
+                      "l3 %s pin already in use", fun);
+               return -EBUSY;
+       }
+       gpio_direction_output(pin, 0);
+       return 0;
+}
+
+static int s3c24xx_uda134x_probe(struct platform_device *pdev)
+{
+       int ret;
+
+       printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
+
+       s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
+       if (s3c24xx_uda134x_l3_pins == NULL) {
+               printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
+                      "unable to find platform data\n");
+               return -ENODEV;
+       }
+       s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
+       s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
+
+       if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
+                                     "data") < 0)
+               return -EBUSY;
+       if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
+                                     "clk") < 0) {
+               gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
+               return -EBUSY;
+       }
+       if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
+                                     "mode") < 0) {
+               gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
+               gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
+               return -EBUSY;
+       }
+
+       s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!s3c24xx_uda134x_snd_device) {
+               printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
+                      "Unable to register\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(s3c24xx_uda134x_snd_device,
+                            &s3c24xx_uda134x_snd_devdata);
+       s3c24xx_uda134x_snd_devdata.dev = &s3c24xx_uda134x_snd_device->dev;
+       ret = platform_device_add(s3c24xx_uda134x_snd_device);
+       if (ret) {
+               printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
+               platform_device_put(s3c24xx_uda134x_snd_device);
+       }
+
+       return ret;
+}
+
+static int s3c24xx_uda134x_remove(struct platform_device *pdev)
+{
+       platform_device_unregister(s3c24xx_uda134x_snd_device);
+       gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
+       gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
+       gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
+       return 0;
+}
+
+static struct platform_driver s3c24xx_uda134x_driver = {
+       .probe  = s3c24xx_uda134x_probe,
+       .remove = s3c24xx_uda134x_remove,
+       .driver = {
+               .name = "s3c24xx_uda134x",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init s3c24xx_uda134x_init(void)
+{
+       return platform_driver_register(&s3c24xx_uda134x_driver);
+}
+
+static void __exit s3c24xx_uda134x_exit(void)
+{
+       platform_driver_unregister(&s3c24xx_uda134x_driver);
+}
+
+
+module_init(s3c24xx_uda134x_init);
+module_exit(s3c24xx_uda134x_exit);
+
+MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
+MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
+MODULE_LICENSE("GPL");
index 8515d6f..a2a4f53 100644 (file)
@@ -23,7 +23,7 @@
 #include "s3c24xx-pcm.h"
 #include "s3c24xx-ac97.h"
 
-static struct snd_soc_machine smdk2443;
+static struct snd_soc_card smdk2443;
 
 static struct snd_soc_dai_link smdk2443_dai[] = {
 {
@@ -34,15 +34,15 @@ static struct snd_soc_dai_link smdk2443_dai[] = {
 },
 };
 
-static struct snd_soc_machine smdk2443 = {
+static struct snd_soc_card smdk2443 = {
        .name = "SMDK2443",
+       .platform = &s3c24xx_soc_platform,
        .dai_link = smdk2443_dai,
        .num_links = ARRAY_SIZE(smdk2443_dai),
 };
 
 static struct snd_soc_device smdk2443_snd_ac97_devdata = {
-       .machine = &smdk2443,
-       .platform = &s3c24xx_soc_platform,
+       .card = &smdk2443,
        .codec_dev = &soc_codec_dev_ac97,
 };
 
index 9faa126..0dad3a0 100644 (file)
@@ -348,6 +348,18 @@ struct snd_soc_platform sh7760_soc_platform = {
 };
 EXPORT_SYMBOL_GPL(sh7760_soc_platform);
 
+static int __init sh7760_soc_platform_init(void)
+{
+       return snd_soc_register_platform(&sh7760_soc_platform);
+}
+module_init(sh7760_soc_platform_init);
+
+static void __exit sh7760_soc_platform_exit(void)
+{
+       snd_soc_unregister_platform(&sh7760_soc_platform);
+}
+module_exit(sh7760_soc_platform_exit);
+
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver");
 MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
index df7bc34..eab3183 100644 (file)
@@ -236,7 +236,8 @@ struct snd_ac97_bus_ops soc_ac97_ops = {
 EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 static int hac_hw_params(struct snd_pcm_substream *substream,
-                        struct snd_pcm_hw_params *params)
+                        struct snd_pcm_hw_params *params,
+                        struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct hac_priv *hac = &hac_cpu_data[rtd->dai->cpu_dai->id];
@@ -270,7 +271,7 @@ struct snd_soc_dai sh4_hac_dai[] = {
 {
        .name                   = "HAC0",
        .id                     = 0,
-       .type                   = SND_SOC_DAI_AC97,
+       .ac97_control           = 1,
        .playback = {
                .rates          = AC97_RATES,
                .formats        = AC97_FMTS,
@@ -290,8 +291,8 @@ struct snd_soc_dai sh4_hac_dai[] = {
 #ifdef CONFIG_CPU_SUBTYPE_SH7760
 {
        .name                   = "HAC1",
+       .ac97_control           = 1,
        .id                     = 1,
-       .type                   = SND_SOC_DAI_AC97,
        .playback = {
                .rates          = AC97_RATES,
                .formats        = AC97_FMTS,
@@ -313,6 +314,18 @@ struct snd_soc_dai sh4_hac_dai[] = {
 };
 EXPORT_SYMBOL_GPL(sh4_hac_dai);
 
+static int __init sh4_hac_init(void)
+{
+       return snd_soc_register_dais(sh4_hac_dai, ARRAY_SIZE(sh4_hac_dai));
+}
+module_init(sh4_hac_init);
+
+static void __exit sh4_hac_exit(void)
+{
+       snd_soc_unregister_dais(sh4_hac_dai, ARRAY_SIZE(sh4_hac_dai));
+}
+module_exit(sh4_hac_exit);
+
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("SuperH onchip HAC (AC97) audio driver");
 MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
index 92bfaf4..ce7f95b 100644 (file)
@@ -38,15 +38,15 @@ static struct snd_soc_dai_link sh7760_ac97_dai = {
        .ops = NULL,
 };
 
-static struct snd_soc_machine sh7760_ac97_soc_machine  = {
+static struct snd_soc_card sh7760_ac97_soc_machine  = {
        .name = "SH7760 AC97",
+       .platform = &sh7760_soc_platform,
        .dai_link = &sh7760_ac97_dai,
        .num_links = 1,
 };
 
 static struct snd_soc_device sh7760_ac97_snd_devdata = {
-       .machine = &sh7760_ac97_soc_machine,
-       .platform = &sh7760_soc_platform,
+       .card = &sh7760_ac97_soc_machine,
        .codec_dev = &soc_codec_dev_ac97,
 };
 
index 55c3464..d1e5390 100644 (file)
@@ -89,7 +89,8 @@ struct ssi_priv {
  * track usage of the SSI; it is simplex-only so prevent attempts of
  * concurrent playback + capture. FIXME: any locking required?
  */
-static int ssi_startup(struct snd_pcm_substream *substream)
+static int ssi_startup(struct snd_pcm_substream *substream,
+                      struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
@@ -101,7 +102,8 @@ static int ssi_startup(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static void ssi_shutdown(struct snd_pcm_substream *substream)
+static void ssi_shutdown(struct snd_pcm_substream *substream,
+                        struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
@@ -109,7 +111,8 @@ static void ssi_shutdown(struct snd_pcm_substream *substream)
        ssi->inuse = 0;
 }
 
-static int ssi_trigger(struct snd_pcm_substream *substream, int cmd)
+static int ssi_trigger(struct snd_pcm_substream *substream, int cmd,
+                      struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
@@ -129,7 +132,8 @@ static int ssi_trigger(struct snd_pcm_substream *substream, int cmd)
 }
 
 static int ssi_hw_params(struct snd_pcm_substream *substream,
-                        struct snd_pcm_hw_params *params)
+                        struct snd_pcm_hw_params *params,
+                        struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
@@ -336,7 +340,6 @@ struct snd_soc_dai sh4_ssi_dai[] = {
 {
        .name                   = "SSI0",
        .id                     = 0,
-       .type                   = SND_SOC_DAI_I2S,
        .playback = {
                .rates          = SSI_RATES,
                .formats        = SSI_FMTS,
@@ -354,8 +357,6 @@ struct snd_soc_dai sh4_ssi_dai[] = {
                .shutdown       = ssi_shutdown,
                .trigger        = ssi_trigger,
                .hw_params      = ssi_hw_params,
-       },
-       .dai_ops = {
                .set_sysclk     = ssi_set_sysclk,
                .set_clkdiv     = ssi_set_clkdiv,
                .set_fmt        = ssi_set_fmt,
@@ -365,7 +366,6 @@ struct snd_soc_dai sh4_ssi_dai[] = {
 {
        .name                   = "SSI1",
        .id                     = 1,
-       .type                   = SND_SOC_DAI_I2S,
        .playback = {
                .rates          = SSI_RATES,
                .formats        = SSI_FMTS,
@@ -383,8 +383,6 @@ struct snd_soc_dai sh4_ssi_dai[] = {
                .shutdown       = ssi_shutdown,
                .trigger        = ssi_trigger,
                .hw_params      = ssi_hw_params,
-       },
-       .dai_ops = {
                .set_sysclk     = ssi_set_sysclk,
                .set_clkdiv     = ssi_set_clkdiv,
                .set_fmt        = ssi_set_fmt,
@@ -394,6 +392,18 @@ struct snd_soc_dai sh4_ssi_dai[] = {
 };
 EXPORT_SYMBOL_GPL(sh4_ssi_dai);
 
+static int __init sh4_ssi_init(void)
+{
+       return snd_soc_register_dais(sh4_ssi_dai, ARRAY_SIZE(sh4_ssi_dai));
+}
+module_init(sh4_ssi_init);
+
+static void __exit sh4_ssi_exit(void)
+{
+       snd_soc_unregister_dais(sh4_ssi_dai, ARRAY_SIZE(sh4_ssi_dai));
+}
+module_exit(sh4_ssi_exit);
+
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("SuperH onchip SSI (I2S) audio driver");
 MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
index 16c7453..b098c0b 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/bitops.h>
+#include <linux/debugfs.h>
 #include <linux/platform_device.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc-dapm.h>
 #include <sound/initval.h>
 
-/* debug */
-#define SOC_DEBUG 0
-#if SOC_DEBUG
-#define dbg(format, arg...) printk(format, ## arg)
-#else
-#define dbg(format, arg...)
-#endif
-
 static DEFINE_MUTEX(pcm_mutex);
 static DEFINE_MUTEX(io_mutex);
 static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
 
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_root;
+#endif
+
+static DEFINE_MUTEX(client_mutex);
+static LIST_HEAD(card_list);
+static LIST_HEAD(dai_list);
+static LIST_HEAD(platform_list);
+static LIST_HEAD(codec_list);
+
+static int snd_soc_register_card(struct snd_soc_card *card);
+static int snd_soc_unregister_card(struct snd_soc_card *card);
+
 /*
  * This is a timeout to do a DAPM powerdown after a stream is closed().
  * It can be used to eliminate pops between different playback streams, e.g.
@@ -107,20 +113,6 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
 }
 #endif
 
-static inline const char *get_dai_name(int type)
-{
-       switch (type) {
-       case SND_SOC_DAI_AC97_BUS:
-       case SND_SOC_DAI_AC97:
-               return "AC97";
-       case SND_SOC_DAI_I2S:
-               return "I2S";
-       case SND_SOC_DAI_PCM:
-               return "PCM";
-       }
-       return NULL;
-}
-
 /*
  * Called by ALSA when a PCM substream is opened, the runtime->hw record is
  * then initialized and any private data can be allocated. This also calls
@@ -130,9 +122,10 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_card *card = socdev->card;
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_soc_dai_link *machine = rtd->dai;
-       struct snd_soc_platform *platform = socdev->platform;
+       struct snd_soc_platform *platform = card->platform;
        struct snd_soc_dai *cpu_dai = machine->cpu_dai;
        struct snd_soc_dai *codec_dai = machine->codec_dai;
        int ret = 0;
@@ -141,7 +134,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
 
        /* startup the audio subsystem */
        if (cpu_dai->ops.startup) {
-               ret = cpu_dai->ops.startup(substream);
+               ret = cpu_dai->ops.startup(substream, cpu_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: can't open interface %s\n",
                                cpu_dai->name);
@@ -158,7 +151,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
        }
 
        if (codec_dai->ops.startup) {
-               ret = codec_dai->ops.startup(substream);
+               ret = codec_dai->ops.startup(substream, codec_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: can't open codec %s\n",
                                codec_dai->name);
@@ -228,12 +221,12 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
                goto machine_err;
        }
 
-       dbg("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
-       dbg("asoc: rate mask 0x%x\n", runtime->hw.rates);
-       dbg("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
-               runtime->hw.channels_max);
-       dbg("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
-               runtime->hw.rate_max);
+       pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
+       pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
+       pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
+                runtime->hw.channels_max);
+       pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
+                runtime->hw.rate_max);
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                cpu_dai->playback.active = codec_dai->playback.active = 1;
@@ -255,7 +248,7 @@ codec_dai_err:
 
 platform_err:
        if (cpu_dai->ops.shutdown)
-               cpu_dai->ops.shutdown(substream);
+               cpu_dai->ops.shutdown(substream, cpu_dai);
 out:
        mutex_unlock(&pcm_mutex);
        return ret;
@@ -268,8 +261,9 @@ out:
  */
 static void close_delayed_work(struct work_struct *work)
 {
-       struct snd_soc_device *socdev =
-               container_of(work, struct snd_soc_device, delayed_work.work);
+       struct snd_soc_card *card = container_of(work, struct snd_soc_card,
+                                                delayed_work.work);
+       struct snd_soc_device *socdev = card->socdev;
        struct snd_soc_codec *codec = socdev->codec;
        struct snd_soc_dai *codec_dai;
        int i;
@@ -278,18 +272,18 @@ static void close_delayed_work(struct work_struct *work)
        for (i = 0; i < codec->num_dai; i++) {
                codec_dai = &codec->dai[i];
 
-               dbg("pop wq checking: %s status: %s waiting: %s\n",
-                       codec_dai->playback.stream_name,
-                       codec_dai->playback.active ? "active" : "inactive",
-                       codec_dai->pop_wait ? "yes" : "no");
+               pr_debug("pop wq checking: %s status: %s waiting: %s\n",
+                        codec_dai->playback.stream_name,
+                        codec_dai->playback.active ? "active" : "inactive",
+                        codec_dai->pop_wait ? "yes" : "no");
 
                /* are we waiting on this codec DAI stream */
                if (codec_dai->pop_wait == 1) {
 
                        /* Reduce power if no longer active */
                        if (codec->active == 0) {
-                               dbg("pop wq D1 %s %s\n", codec->name,
-                                       codec_dai->playback.stream_name);
+                               pr_debug("pop wq D1 %s %s\n", codec->name,
+                                        codec_dai->playback.stream_name);
                                snd_soc_dapm_set_bias_level(socdev,
                                        SND_SOC_BIAS_PREPARE);
                        }
@@ -301,8 +295,8 @@ static void close_delayed_work(struct work_struct *work)
 
                        /* Fall into standby if no longer active */
                        if (codec->active == 0) {
-                               dbg("pop wq D3 %s %s\n", codec->name,
-                                       codec_dai->playback.stream_name);
+                               pr_debug("pop wq D3 %s %s\n", codec->name,
+                                        codec_dai->playback.stream_name);
                                snd_soc_dapm_set_bias_level(socdev,
                                        SND_SOC_BIAS_STANDBY);
                        }
@@ -320,8 +314,9 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_card *card = socdev->card;
        struct snd_soc_dai_link *machine = rtd->dai;
-       struct snd_soc_platform *platform = socdev->platform;
+       struct snd_soc_platform *platform = card->platform;
        struct snd_soc_dai *cpu_dai = machine->cpu_dai;
        struct snd_soc_dai *codec_dai = machine->codec_dai;
        struct snd_soc_codec *codec = socdev->codec;
@@ -346,10 +341,10 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
                snd_soc_dai_digital_mute(codec_dai, 1);
 
        if (cpu_dai->ops.shutdown)
-               cpu_dai->ops.shutdown(substream);
+               cpu_dai->ops.shutdown(substream, cpu_dai);
 
        if (codec_dai->ops.shutdown)
-               codec_dai->ops.shutdown(substream);
+               codec_dai->ops.shutdown(substream, codec_dai);
 
        if (machine->ops && machine->ops->shutdown)
                machine->ops->shutdown(substream);
@@ -361,7 +356,7 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                /* start delayed pop wq here for playback streams */
                codec_dai->pop_wait = 1;
-               schedule_delayed_work(&socdev->delayed_work,
+               schedule_delayed_work(&card->delayed_work,
                        msecs_to_jiffies(pmdown_time));
        } else {
                /* capture streams can be powered down now */
@@ -387,8 +382,9 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_card *card = socdev->card;
        struct snd_soc_dai_link *machine = rtd->dai;
-       struct snd_soc_platform *platform = socdev->platform;
+       struct snd_soc_platform *platform = card->platform;
        struct snd_soc_dai *cpu_dai = machine->cpu_dai;
        struct snd_soc_dai *codec_dai = machine->codec_dai;
        struct snd_soc_codec *codec = socdev->codec;
@@ -413,7 +409,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
        }
 
        if (codec_dai->ops.prepare) {
-               ret = codec_dai->ops.prepare(substream);
+               ret = codec_dai->ops.prepare(substream, codec_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: codec DAI prepare error\n");
                        goto out;
@@ -421,58 +417,49 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
        }
 
        if (cpu_dai->ops.prepare) {
-               ret = cpu_dai->ops.prepare(substream);
+               ret = cpu_dai->ops.prepare(substream, cpu_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: cpu DAI prepare error\n");
                        goto out;
                }
        }
 
-       /* we only want to start a DAPM playback stream if we are not waiting
-        * on an existing one stopping */
-       if (codec_dai->pop_wait) {
-               /* we are waiting for the delayed work to start */
-               if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-                               snd_soc_dapm_stream_event(socdev->codec,
-                                       codec_dai->capture.stream_name,
-                                       SND_SOC_DAPM_STREAM_START);
-               else {
-                       codec_dai->pop_wait = 0;
-                       cancel_delayed_work(&socdev->delayed_work);
-                       snd_soc_dai_digital_mute(codec_dai, 0);
-               }
-       } else {
-               /* no delayed work - do we need to power up codec */
-               if (codec->bias_level != SND_SOC_BIAS_ON) {
+       /* cancel any delayed stream shutdown that is pending */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+           codec_dai->pop_wait) {
+               codec_dai->pop_wait = 0;
+               cancel_delayed_work(&card->delayed_work);
+       }
 
-                       snd_soc_dapm_set_bias_level(socdev,
-                                                   SND_SOC_BIAS_PREPARE);
+       /* do we need to power up codec */
+       if (codec->bias_level != SND_SOC_BIAS_ON) {
+               snd_soc_dapm_set_bias_level(socdev,
+                                           SND_SOC_BIAS_PREPARE);
 
-                       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-                               snd_soc_dapm_stream_event(codec,
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       snd_soc_dapm_stream_event(codec,
                                        codec_dai->playback.stream_name,
                                        SND_SOC_DAPM_STREAM_START);
-                       else
-                               snd_soc_dapm_stream_event(codec,
+               else
+                       snd_soc_dapm_stream_event(codec,
                                        codec_dai->capture.stream_name,
                                        SND_SOC_DAPM_STREAM_START);
 
-                       snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON);
-                       snd_soc_dai_digital_mute(codec_dai, 0);
+               snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON);
+               snd_soc_dai_digital_mute(codec_dai, 0);
 
-               } else {
-                       /* codec already powered - power on widgets */
-                       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-                               snd_soc_dapm_stream_event(codec,
+       } else {
+               /* codec already powered - power on widgets */
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       snd_soc_dapm_stream_event(codec,
                                        codec_dai->playback.stream_name,
                                        SND_SOC_DAPM_STREAM_START);
-                       else
-                               snd_soc_dapm_stream_event(codec,
+               else
+                       snd_soc_dapm_stream_event(codec,
                                        codec_dai->capture.stream_name,
                                        SND_SOC_DAPM_STREAM_START);
 
-                       snd_soc_dai_digital_mute(codec_dai, 0);
-               }
+               snd_soc_dai_digital_mute(codec_dai, 0);
        }
 
 out:
@@ -491,7 +478,8 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
        struct snd_soc_dai_link *machine = rtd->dai;
-       struct snd_soc_platform *platform = socdev->platform;
+       struct snd_soc_card *card = socdev->card;
+       struct snd_soc_platform *platform = card->platform;
        struct snd_soc_dai *cpu_dai = machine->cpu_dai;
        struct snd_soc_dai *codec_dai = machine->codec_dai;
        int ret = 0;
@@ -507,7 +495,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
        }
 
        if (codec_dai->ops.hw_params) {
-               ret = codec_dai->ops.hw_params(substream, params);
+               ret = codec_dai->ops.hw_params(substream, params, codec_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: can't set codec %s hw params\n",
                                codec_dai->name);
@@ -516,7 +504,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
        }
 
        if (cpu_dai->ops.hw_params) {
-               ret = cpu_dai->ops.hw_params(substream, params);
+               ret = cpu_dai->ops.hw_params(substream, params, cpu_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: interface %s hw params failed\n",
                                cpu_dai->name);
@@ -539,11 +527,11 @@ out:
 
 platform_err:
        if (cpu_dai->ops.hw_free)
-               cpu_dai->ops.hw_free(substream);
+               cpu_dai->ops.hw_free(substream, cpu_dai);
 
 interface_err:
        if (codec_dai->ops.hw_free)
-               codec_dai->ops.hw_free(substream);
+               codec_dai->ops.hw_free(substream, codec_dai);
 
 codec_err:
        if (machine->ops && machine->ops->hw_free)
@@ -561,7 +549,8 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
        struct snd_soc_dai_link *machine = rtd->dai;
-       struct snd_soc_platform *platform = socdev->platform;
+       struct snd_soc_card *card = socdev->card;
+       struct snd_soc_platform *platform = card->platform;
        struct snd_soc_dai *cpu_dai = machine->cpu_dai;
        struct snd_soc_dai *codec_dai = machine->codec_dai;
        struct snd_soc_codec *codec = socdev->codec;
@@ -582,10 +571,10 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
 
        /* now free hw params for the DAI's  */
        if (codec_dai->ops.hw_free)
-               codec_dai->ops.hw_free(substream);
+               codec_dai->ops.hw_free(substream, codec_dai);
 
        if (cpu_dai->ops.hw_free)
-               cpu_dai->ops.hw_free(substream);
+               cpu_dai->ops.hw_free(substream, cpu_dai);
 
        mutex_unlock(&pcm_mutex);
        return 0;
@@ -595,14 +584,15 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_card *card= socdev->card;
        struct snd_soc_dai_link *machine = rtd->dai;
-       struct snd_soc_platform *platform = socdev->platform;
+       struct snd_soc_platform *platform = card->platform;
        struct snd_soc_dai *cpu_dai = machine->cpu_dai;
        struct snd_soc_dai *codec_dai = machine->codec_dai;
        int ret;
 
        if (codec_dai->ops.trigger) {
-               ret = codec_dai->ops.trigger(substream, cmd);
+               ret = codec_dai->ops.trigger(substream, cmd, codec_dai);
                if (ret < 0)
                        return ret;
        }
@@ -614,7 +604,7 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        }
 
        if (cpu_dai->ops.trigger) {
-               ret = cpu_dai->ops.trigger(substream, cmd);
+               ret = cpu_dai->ops.trigger(substream, cmd, cpu_dai);
                if (ret < 0)
                        return ret;
        }
@@ -636,8 +626,8 @@ static struct snd_pcm_ops soc_pcm_ops = {
 static int soc_suspend(struct platform_device *pdev, pm_message_t state)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_machine *machine = socdev->machine;
-       struct snd_soc_platform *platform = socdev->platform;
+       struct snd_soc_card *card = socdev->card;
+       struct snd_soc_platform *platform = card->platform;
        struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
        struct snd_soc_codec *codec = socdev->codec;
        int i;
@@ -653,29 +643,29 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
        snd_power_change_state(codec->card, SNDRV_CTL_POWER_D3hot);
 
        /* mute any active DAC's */
-       for (i = 0; i < machine->num_links; i++) {
-               struct snd_soc_dai *dai = machine->dai_link[i].codec_dai;
-               if (dai->dai_ops.digital_mute && dai->playback.active)
-                       dai->dai_ops.digital_mute(dai, 1);
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai *dai = card->dai_link[i].codec_dai;
+               if (dai->ops.digital_mute && dai->playback.active)
+                       dai->ops.digital_mute(dai, 1);
        }
 
        /* suspend all pcms */
-       for (i = 0; i < machine->num_links; i++)
-               snd_pcm_suspend_all(machine->dai_link[i].pcm);
+       for (i = 0; i < card->num_links; i++)
+               snd_pcm_suspend_all(card->dai_link[i].pcm);
 
-       if (machine->suspend_pre)
-               machine->suspend_pre(pdev, state);
+       if (card->suspend_pre)
+               card->suspend_pre(pdev, state);
 
-       for (i = 0; i < machine->num_links; i++) {
-               struct snd_soc_dai  *cpu_dai = machine->dai_link[i].cpu_dai;
-               if (cpu_dai->suspend && cpu_dai->type != SND_SOC_DAI_AC97)
-                       cpu_dai->suspend(pdev, cpu_dai);
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai  *cpu_dai = card->dai_link[i].cpu_dai;
+               if (cpu_dai->suspend && !cpu_dai->ac97_control)
+                       cpu_dai->suspend(cpu_dai);
                if (platform->suspend)
-                       platform->suspend(pdev, cpu_dai);
+                       platform->suspend(cpu_dai);
        }
 
        /* close any waiting streams and save state */
-       run_delayed_work(&socdev->delayed_work);
+       run_delayed_work(&card->delayed_work);
        codec->suspend_bias_level = codec->bias_level;
 
        for (i = 0; i < codec->num_dai; i++) {
@@ -692,14 +682,14 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
        if (codec_dev->suspend)
                codec_dev->suspend(pdev, state);
 
-       for (i = 0; i < machine->num_links; i++) {
-               struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
-               if (cpu_dai->suspend && cpu_dai->type == SND_SOC_DAI_AC97)
-                       cpu_dai->suspend(pdev, cpu_dai);
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
+               if (cpu_dai->suspend && cpu_dai->ac97_control)
+                       cpu_dai->suspend(cpu_dai);
        }
 
-       if (machine->suspend_post)
-               machine->suspend_post(pdev, state);
+       if (card->suspend_post)
+               card->suspend_post(pdev, state);
 
        return 0;
 }
@@ -709,11 +699,11 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
  */
 static void soc_resume_deferred(struct work_struct *work)
 {
-       struct snd_soc_device *socdev = container_of(work,
-                                                    struct snd_soc_device,
-                                                    deferred_resume_work);
-       struct snd_soc_machine *machine = socdev->machine;
-       struct snd_soc_platform *platform = socdev->platform;
+       struct snd_soc_card *card = container_of(work,
+                                                struct snd_soc_card,
+                                                deferred_resume_work);
+       struct snd_soc_device *socdev = card->socdev;
+       struct snd_soc_platform *platform = card->platform;
        struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
        struct snd_soc_codec *codec = socdev->codec;
        struct platform_device *pdev = to_platform_device(socdev->dev);
@@ -723,15 +713,15 @@ static void soc_resume_deferred(struct work_struct *work)
         * so userspace apps are blocked from touching us
         */
 
-       dev_info(socdev->dev, "starting resume work\n");
+       dev_dbg(socdev->dev, "starting resume work\n");
 
-       if (machine->resume_pre)
-               machine->resume_pre(pdev);
+       if (card->resume_pre)
+               card->resume_pre(pdev);
 
-       for (i = 0; i < machine->num_links; i++) {
-               struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
-               if (cpu_dai->resume && cpu_dai->type == SND_SOC_DAI_AC97)
-                       cpu_dai->resume(pdev, cpu_dai);
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
+               if (cpu_dai->resume && cpu_dai->ac97_control)
+                       cpu_dai->resume(cpu_dai);
        }
 
        if (codec_dev->resume)
@@ -749,24 +739,24 @@ static void soc_resume_deferred(struct work_struct *work)
        }
 
        /* unmute any active DACs */
-       for (i = 0; i < machine->num_links; i++) {
-               struct snd_soc_dai *dai = machine->dai_link[i].codec_dai;
-               if (dai->dai_ops.digital_mute && dai->playback.active)
-                       dai->dai_ops.digital_mute(dai, 0);
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai *dai = card->dai_link[i].codec_dai;
+               if (dai->ops.digital_mute && dai->playback.active)
+                       dai->ops.digital_mute(dai, 0);
        }
 
-       for (i = 0; i < machine->num_links; i++) {
-               struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
-               if (cpu_dai->resume && cpu_dai->type != SND_SOC_DAI_AC97)
-                       cpu_dai->resume(pdev, cpu_dai);
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
+               if (cpu_dai->resume && !cpu_dai->ac97_control)
+                       cpu_dai->resume(cpu_dai);
                if (platform->resume)
-                       platform->resume(pdev, cpu_dai);
+                       platform->resume(cpu_dai);
        }
 
-       if (machine->resume_post)
-               machine->resume_post(pdev);
+       if (card->resume_post)
+               card->resume_post(pdev);
 
-       dev_info(socdev->dev, "resume work completed\n");
+       dev_dbg(socdev->dev, "resume work completed\n");
 
        /* userspace can access us now we are back as we were before */
        snd_power_change_state(codec->card, SNDRV_CTL_POWER_D0);
@@ -776,11 +766,12 @@ static void soc_resume_deferred(struct work_struct *work)
 static int soc_resume(struct platform_device *pdev)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_card *card = socdev->card;
 
-       dev_info(socdev->dev, "scheduling resume work\n");
+       dev_dbg(socdev->dev, "scheduling resume work\n");
 
-       if (!schedule_work(&socdev->deferred_resume_work))
-               dev_err(socdev->dev, "work item may be lost\n");
+       if (!schedule_work(&card->deferred_resume_work))
+               dev_err(socdev->dev, "resume work item may be lost\n");
 
        return 0;
 }
@@ -790,23 +781,83 @@ static int soc_resume(struct platform_device *pdev)
 #define soc_resume     NULL
 #endif
 
-/* probes a new socdev */
-static int soc_probe(struct platform_device *pdev)
+static void snd_soc_instantiate_card(struct snd_soc_card *card)
 {
-       int ret = 0, i;
-       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_machine *machine = socdev->machine;
-       struct snd_soc_platform *platform = socdev->platform;
-       struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
+       struct platform_device *pdev = container_of(card->dev,
+                                                   struct platform_device,
+                                                   dev);
+       struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev;
+       struct snd_soc_platform *platform;
+       struct snd_soc_dai *dai;
+       int i, found, ret, ac97;
+
+       if (card->instantiated)
+               return;
+
+       found = 0;
+       list_for_each_entry(platform, &platform_list, list)
+               if (card->platform == platform) {
+                       found = 1;
+                       break;
+               }
+       if (!found) {
+               dev_dbg(card->dev, "Platform %s not registered\n",
+                       card->platform->name);
+               return;
+       }
+
+       ac97 = 0;
+       for (i = 0; i < card->num_links; i++) {
+               found = 0;
+               list_for_each_entry(dai, &dai_list, list)
+                       if (card->dai_link[i].cpu_dai == dai) {
+                               found = 1;
+                               break;
+                       }
+               if (!found) {
+                       dev_dbg(card->dev, "DAI %s not registered\n",
+                               card->dai_link[i].cpu_dai->name);
+                       return;
+               }
 
-       if (machine->probe) {
-               ret = machine->probe(pdev);
+               if (card->dai_link[i].cpu_dai->ac97_control)
+                       ac97 = 1;
+       }
+
+       /* If we have AC97 in the system then don't wait for the
+        * codec.  This will need revisiting if we have to handle
+        * systems with mixed AC97 and non-AC97 parts.  Only check for
+        * DAIs currently; we can't do this per link since some AC97
+        * codecs have non-AC97 DAIs.
+        */
+       if (!ac97)
+               for (i = 0; i < card->num_links; i++) {
+                       found = 0;
+                       list_for_each_entry(dai, &dai_list, list)
+                               if (card->dai_link[i].codec_dai == dai) {
+                                       found = 1;
+                                       break;
+                               }
+                       if (!found) {
+                               dev_dbg(card->dev, "DAI %s not registered\n",
+                                       card->dai_link[i].codec_dai->name);
+                               return;
+                       }
+               }
+
+       /* Note that we do not current check for codec components */
+
+       dev_dbg(card->dev, "All components present, instantiating\n");
+
+       /* Found everything, bring it up */
+       if (card->probe) {
+               ret = card->probe(pdev);
                if (ret < 0)
-                       return ret;
+                       return;
        }
 
-       for (i = 0; i < machine->num_links; i++) {
-               struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
                if (cpu_dai->probe) {
                        ret = cpu_dai->probe(pdev, cpu_dai);
                        if (ret < 0)
@@ -827,13 +878,15 @@ static int soc_probe(struct platform_device *pdev)
        }
 
        /* DAPM stream work */
-       INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work);
+       INIT_DELAYED_WORK(&card->delayed_work, close_delayed_work);
 #ifdef CONFIG_PM
        /* deferred resume work */
-       INIT_WORK(&socdev->deferred_resume_work, soc_resume_deferred);
+       INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
 #endif
 
-       return 0;
+       card->instantiated = 1;
+
+       return;
 
 platform_err:
        if (codec_dev->remove)
@@ -841,15 +894,45 @@ platform_err:
 
 cpu_dai_err:
        for (i--; i >= 0; i--) {
-               struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+               struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
                if (cpu_dai->remove)
                        cpu_dai->remove(pdev, cpu_dai);
        }
 
-       if (machine->remove)
-               machine->remove(pdev);
+       if (card->remove)
+               card->remove(pdev);
+}
 
-       return ret;
+/*
+ * Attempt to initialise any uninitalised cards.  Must be called with
+ * client_mutex.
+ */
+static void snd_soc_instantiate_cards(void)
+{
+       struct snd_soc_card *card;
+       list_for_each_entry(card, &card_list, list)
+               snd_soc_instantiate_card(card);
+}
+
+/* probes a new socdev */
+static int soc_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_card *card = socdev->card;
+
+       /* Bodge while we push things out of socdev */
+       card->socdev = socdev;
+
+       /* Bodge while we unpick instantiation */
+       card->dev = &pdev->dev;
+       ret = snd_soc_register_card(card);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "Failed to register card\n");
+               return ret;
+       }
+
+       return 0;
 }
 
 /* removes a socdev */
@@ -857,11 +940,11 @@ static int soc_remove(struct platform_device *pdev)
 {
        int i;
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_machine *machine = socdev->machine;
-       struct snd_soc_platform *platform = socdev->platform;
+       struct snd_soc_card *card = socdev->card;
+       struct snd_soc_platform *platform = card->platform;
        struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
 
-       run_delayed_work(&socdev->delayed_work);
+       run_delayed_work(&card->delayed_work);
 
        if (platform->remove)
                platform->remove(pdev);
@@ -869,14 +952,16 @@ static int soc_remove(struct platform_device *pdev)
        if (codec_dev->remove)
                codec_dev->remove(pdev);
 
-       for (i = 0; i < machine->num_links; i++) {
-               struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
                if (cpu_dai->remove)
                        cpu_dai->remove(pdev, cpu_dai);
        }
 
-       if (machine->remove)
-               machine->remove(pdev);
+       if (card->remove)
+               card->remove(pdev);
+
+       snd_soc_unregister_card(card);
 
        return 0;
 }
@@ -898,6 +983,8 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
        struct snd_soc_dai_link *dai_link, int num)
 {
        struct snd_soc_codec *codec = socdev->codec;
+       struct snd_soc_card *card = socdev->card;
+       struct snd_soc_platform *platform = card->platform;
        struct snd_soc_dai *codec_dai = dai_link->codec_dai;
        struct snd_soc_dai *cpu_dai = dai_link->cpu_dai;
        struct snd_soc_pcm_runtime *rtd;
@@ -914,8 +1001,8 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
        codec_dai->codec = socdev->codec;
 
        /* check client and interface hw capabilities */
-       sprintf(new_name, "%s %s-%s-%d", dai_link->stream_name, codec_dai->name,
-               get_dai_name(cpu_dai->type), num);
+       sprintf(new_name, "%s %s-%d", dai_link->stream_name, codec_dai->name,
+               num);
 
        if (codec_dai->playback.channels_min)
                playback = 1;
@@ -933,13 +1020,13 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
 
        dai_link->pcm = pcm;
        pcm->private_data = rtd;
-       soc_pcm_ops.mmap = socdev->platform->pcm_ops->mmap;
-       soc_pcm_ops.pointer = socdev->platform->pcm_ops->pointer;
-       soc_pcm_ops.ioctl = socdev->platform->pcm_ops->ioctl;
-       soc_pcm_ops.copy = socdev->platform->pcm_ops->copy;
-       soc_pcm_ops.silence = socdev->platform->pcm_ops->silence;
-       soc_pcm_ops.ack = socdev->platform->pcm_ops->ack;
-       soc_pcm_ops.page = socdev->platform->pcm_ops->page;
+       soc_pcm_ops.mmap = platform->pcm_ops->mmap;
+       soc_pcm_ops.pointer = platform->pcm_ops->pointer;
+       soc_pcm_ops.ioctl = platform->pcm_ops->ioctl;
+       soc_pcm_ops.copy = platform->pcm_ops->copy;
+       soc_pcm_ops.silence = platform->pcm_ops->silence;
+       soc_pcm_ops.ack = platform->pcm_ops->ack;
+       soc_pcm_ops.page = platform->pcm_ops->page;
 
        if (playback)
                snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
@@ -947,24 +1034,22 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
        if (capture)
                snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
 
-       ret = socdev->platform->pcm_new(codec->card, codec_dai, pcm);
+       ret = platform->pcm_new(codec->card, codec_dai, pcm);
        if (ret < 0) {
                printk(KERN_ERR "asoc: platform pcm constructor failed\n");
                kfree(rtd);
                return ret;
        }
 
-       pcm->private_free = socdev->platform->pcm_free;
+       pcm->private_free = platform->pcm_free;
        printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
                cpu_dai->name);
        return ret;
 }
 
 /* codec register dump */
-static ssize_t codec_reg_show(struct device *dev,
-       struct device_attribute *attr, char *buf)
+static ssize_t soc_codec_reg_show(struct snd_soc_device *devdata, char *buf)
 {
-       struct snd_soc_device *devdata = dev_get_drvdata(dev);
        struct snd_soc_codec *codec = devdata->codec;
        int i, step = 1, count = 0;
 
@@ -1001,8 +1086,110 @@ static ssize_t codec_reg_show(struct device *dev,
 
        return count;
 }
+static ssize_t codec_reg_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct snd_soc_device *devdata = dev_get_drvdata(dev);
+       return soc_codec_reg_show(devdata, buf);
+}
+
 static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
 
+#ifdef CONFIG_DEBUG_FS
+static int codec_reg_open_file(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
+                              size_t count, loff_t *ppos)
+{
+       ssize_t ret;
+       struct snd_soc_codec *codec = file->private_data;
+       struct device *card_dev = codec->card->dev;
+       struct snd_soc_device *devdata = card_dev->driver_data;
+       char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       ret = soc_codec_reg_show(devdata, buf);
+       if (ret >= 0)
+               ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+       kfree(buf);
+       return ret;
+}
+
+static ssize_t codec_reg_write_file(struct file *file,
+               const char __user *user_buf, size_t count, loff_t *ppos)
+{
+       char buf[32];
+       int buf_size;
+       char *start = buf;
+       unsigned long reg, value;
+       int step = 1;
+       struct snd_soc_codec *codec = file->private_data;
+
+       buf_size = min(count, (sizeof(buf)-1));
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+       buf[buf_size] = 0;
+
+       if (codec->reg_cache_step)
+               step = codec->reg_cache_step;
+
+       while (*start == ' ')
+               start++;
+       reg = simple_strtoul(start, &start, 16);
+       if ((reg >= codec->reg_cache_size) || (reg % step))
+               return -EINVAL;
+       while (*start == ' ')
+               start++;
+       if (strict_strtoul(start, 16, &value))
+               return -EINVAL;
+       codec->write(codec, reg, value);
+       return buf_size;
+}
+
+static const struct file_operations codec_reg_fops = {
+       .open = codec_reg_open_file,
+       .read = codec_reg_read_file,
+       .write = codec_reg_write_file,
+};
+
+static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
+{
+       codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
+                                                debugfs_root, codec,
+                                                &codec_reg_fops);
+       if (!codec->debugfs_reg)
+               printk(KERN_WARNING
+                      "ASoC: Failed to create codec register debugfs file\n");
+
+       codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744,
+                                                    debugfs_root,
+                                                    &codec->pop_time);
+       if (!codec->debugfs_pop_time)
+               printk(KERN_WARNING
+                      "Failed to create pop time debugfs file\n");
+}
+
+static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
+{
+       debugfs_remove(codec->debugfs_pop_time);
+       debugfs_remove(codec->debugfs_reg);
+}
+
+#else
+
+static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec)
+{
+}
+
+static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
+{
+}
+#endif
+
 /**
  * snd_soc_new_ac97_codec - initailise AC97 device
  * @codec: audio codec
@@ -1121,7 +1308,7 @@ EXPORT_SYMBOL_GPL(snd_soc_test_bits);
 int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
 {
        struct snd_soc_codec *codec = socdev->codec;
-       struct snd_soc_machine *machine = socdev->machine;
+       struct snd_soc_card *card = socdev->card;
        int ret = 0, i;
 
        mutex_lock(&codec->mutex);
@@ -1140,11 +1327,11 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
        strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));
 
        /* create the pcms */
-       for (i = 0; i < machine->num_links; i++) {
-               ret = soc_new_pcm(socdev, &machine->dai_link[i], i);
+       for (i = 0; i < card->num_links; i++) {
+               ret = soc_new_pcm(socdev, &card->dai_link[i], i);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: can't create pcm %s\n",
-                               machine->dai_link[i].stream_name);
+                               card->dai_link[i].stream_name);
                        mutex_unlock(&codec->mutex);
                        return ret;
                }
@@ -1156,7 +1343,7 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
 EXPORT_SYMBOL_GPL(snd_soc_new_pcms);
 
 /**
- * snd_soc_register_card - register sound card
+ * snd_soc_init_card - register sound card
  * @socdev: the SoC audio device
  *
  * Register a SoC sound card. Also registers an AC97 device if the
@@ -1164,29 +1351,28 @@ EXPORT_SYMBOL_GPL(snd_soc_new_pcms);
  *
  * Returns 0 for success, else error.
  */
-int snd_soc_register_card(struct snd_soc_device *socdev)
+int snd_soc_init_card(struct snd_soc_device *socdev)
 {
        struct snd_soc_codec *codec = socdev->codec;
-       struct snd_soc_machine *machine = socdev->machine;
+       struct snd_soc_card *card = socdev->card;
        int ret = 0, i, ac97 = 0, err = 0;
 
-       for (i = 0; i < machine->num_links; i++) {
-               if (socdev->machine->dai_link[i].init) {
-                       err = socdev->machine->dai_link[i].init(codec);
+       for (i = 0; i < card->num_links; i++) {
+               if (card->dai_link[i].init) {
+                       err = card->dai_link[i].init(codec);
                        if (err < 0) {
                                printk(KERN_ERR "asoc: failed to init %s\n",
-                                       socdev->machine->dai_link[i].stream_name);
+                                       card->dai_link[i].stream_name);
                                continue;
                        }
                }
-               if (socdev->machine->dai_link[i].codec_dai->type ==
-                       SND_SOC_DAI_AC97_BUS)
+               if (card->dai_link[i].codec_dai->ac97_control)
                        ac97 = 1;
        }
        snprintf(codec->card->shortname, sizeof(codec->card->shortname),
-                "%s", machine->name);
+                "%s",  card->name);
        snprintf(codec->card->longname, sizeof(codec->card->longname),
-                "%s (%s)", machine->name, codec->name);
+                "%s (%s)", card->name, codec->name);
 
        ret = snd_card_register(codec->card);
        if (ret < 0) {
@@ -1216,12 +1402,13 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
        if (err < 0)
                printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
 
+       soc_init_codec_debugfs(socdev->codec);
        mutex_unlock(&codec->mutex);
 
 out:
        return ret;
 }
-EXPORT_SYMBOL_GPL(snd_soc_register_card);
+EXPORT_SYMBOL_GPL(snd_soc_init_card);
 
 /**
  * snd_soc_free_pcms - free sound card and pcms
@@ -1239,10 +1426,11 @@ void snd_soc_free_pcms(struct snd_soc_device *socdev)
 #endif
 
        mutex_lock(&codec->mutex);
+       soc_cleanup_codec_debugfs(socdev->codec);
 #ifdef CONFIG_SND_SOC_AC97_BUS
        for (i = 0; i < codec->num_dai; i++) {
                codec_dai = &codec->dai[i];
-               if (codec_dai->type == SND_SOC_DAI_AC97_BUS && codec->ac97) {
+               if (codec_dai->ac97_control && codec->ac97) {
                        soc_ac97_dev_unregister(codec);
                        goto free_card;
                }
@@ -1756,8 +1944,8 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
 int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
        unsigned int freq, int dir)
 {
-       if (dai->dai_ops.set_sysclk)
-               return dai->dai_ops.set_sysclk(dai, clk_id, freq, dir);
+       if (dai->ops.set_sysclk)
+               return dai->ops.set_sysclk(dai, clk_id, freq, dir);
        else
                return -EINVAL;
 }
@@ -1776,8 +1964,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
 int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
        int div_id, int div)
 {
-       if (dai->dai_ops.set_clkdiv)
-               return dai->dai_ops.set_clkdiv(dai, div_id, div);
+       if (dai->ops.set_clkdiv)
+               return dai->ops.set_clkdiv(dai, div_id, div);
        else
                return -EINVAL;
 }
@@ -1795,8 +1983,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
 int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
        int pll_id, unsigned int freq_in, unsigned int freq_out)
 {
-       if (dai->dai_ops.set_pll)
-               return dai->dai_ops.set_pll(dai, pll_id, freq_in, freq_out);
+       if (dai->ops.set_pll)
+               return dai->ops.set_pll(dai, pll_id, freq_in, freq_out);
        else
                return -EINVAL;
 }
@@ -1805,15 +1993,14 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
 /**
  * snd_soc_dai_set_fmt - configure DAI hardware audio format.
  * @dai: DAI
- * @clk_id: DAI specific clock ID
  * @fmt: SND_SOC_DAIFMT_ format value.
  *
  * Configures the DAI hardware format and clocking.
  */
 int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
-       if (dai->dai_ops.set_fmt)
-               return dai->dai_ops.set_fmt(dai, fmt);
+       if (dai->ops.set_fmt)
+               return dai->ops.set_fmt(dai, fmt);
        else
                return -EINVAL;
 }
@@ -1831,8 +2018,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
        unsigned int mask, int slots)
 {
-       if (dai->dai_ops.set_sysclk)
-               return dai->dai_ops.set_tdm_slot(dai, mask, slots);
+       if (dai->ops.set_sysclk)
+               return dai->ops.set_tdm_slot(dai, mask, slots);
        else
                return -EINVAL;
 }
@@ -1847,8 +2034,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
  */
 int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate)
 {
-       if (dai->dai_ops.set_sysclk)
-               return dai->dai_ops.set_tristate(dai, tristate);
+       if (dai->ops.set_sysclk)
+               return dai->ops.set_tristate(dai, tristate);
        else
                return -EINVAL;
 }
@@ -1863,21 +2050,242 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate);
  */
 int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
 {
-       if (dai->dai_ops.digital_mute)
-               return dai->dai_ops.digital_mute(dai, mute);
+       if (dai->ops.digital_mute)
+               return dai->ops.digital_mute(dai, mute);
        else
                return -EINVAL;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
 
-static int __devinit snd_soc_init(void)
+/**
+ * snd_soc_register_card - Register a card with the ASoC core
+ *
+ * @param card Card to register
+ *
+ * Note that currently this is an internal only function: it will be
+ * exposed to machine drivers after further backporting of ASoC v2
+ * registration APIs.
+ */
+static int snd_soc_register_card(struct snd_soc_card *card)
+{
+       if (!card->name || !card->dev)
+               return -EINVAL;
+
+       INIT_LIST_HEAD(&card->list);
+       card->instantiated = 0;
+
+       mutex_lock(&client_mutex);
+       list_add(&card->list, &card_list);
+       snd_soc_instantiate_cards();
+       mutex_unlock(&client_mutex);
+
+       dev_dbg(card->dev, "Registered card '%s'\n", card->name);
+
+       return 0;
+}
+
+/**
+ * snd_soc_unregister_card - Unregister a card with the ASoC core
+ *
+ * @param card Card to unregister
+ *
+ * Note that currently this is an internal only function: it will be
+ * exposed to machine drivers after further backporting of ASoC v2
+ * registration APIs.
+ */
+static int snd_soc_unregister_card(struct snd_soc_card *card)
+{
+       mutex_lock(&client_mutex);
+       list_del(&card->list);
+       mutex_unlock(&client_mutex);
+
+       dev_dbg(card->dev, "Unregistered card '%s'\n", card->name);
+
+       return 0;
+}
+
+/**
+ * snd_soc_register_dai - Register a DAI with the ASoC core
+ *
+ * @param dai DAI to register
+ */
+int snd_soc_register_dai(struct snd_soc_dai *dai)
+{
+       if (!dai->name)
+               return -EINVAL;
+
+       /* The device should become mandatory over time */
+       if (!dai->dev)
+               printk(KERN_WARNING "No device for DAI %s\n", dai->name);
+
+       INIT_LIST_HEAD(&dai->list);
+
+       mutex_lock(&client_mutex);
+       list_add(&dai->list, &dai_list);
+       snd_soc_instantiate_cards();
+       mutex_unlock(&client_mutex);
+
+       pr_debug("Registered DAI '%s'\n", dai->name);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_dai);
+
+/**
+ * snd_soc_unregister_dai - Unregister a DAI from the ASoC core
+ *
+ * @param dai DAI to unregister
+ */
+void snd_soc_unregister_dai(struct snd_soc_dai *dai)
+{
+       mutex_lock(&client_mutex);
+       list_del(&dai->list);
+       mutex_unlock(&client_mutex);
+
+       pr_debug("Unregistered DAI '%s'\n", dai->name);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_dai);
+
+/**
+ * snd_soc_register_dais - Register multiple DAIs with the ASoC core
+ *
+ * @param dai Array of DAIs to register
+ * @param count Number of DAIs
+ */
+int snd_soc_register_dais(struct snd_soc_dai *dai, size_t count)
+{
+       int i, ret;
+
+       for (i = 0; i < count; i++) {
+               ret = snd_soc_register_dai(&dai[i]);
+               if (ret != 0)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       for (i--; i >= 0; i--)
+               snd_soc_unregister_dai(&dai[i]);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_dais);
+
+/**
+ * snd_soc_unregister_dais - Unregister multiple DAIs from the ASoC core
+ *
+ * @param dai Array of DAIs to unregister
+ * @param count Number of DAIs
+ */
+void snd_soc_unregister_dais(struct snd_soc_dai *dai, size_t count)
+{
+       int i;
+
+       for (i = 0; i < count; i++)
+               snd_soc_unregister_dai(&dai[i]);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_dais);
+
+/**
+ * snd_soc_register_platform - Register a platform with the ASoC core
+ *
+ * @param platform platform to register
+ */
+int snd_soc_register_platform(struct snd_soc_platform *platform)
+{
+       if (!platform->name)
+               return -EINVAL;
+
+       INIT_LIST_HEAD(&platform->list);
+
+       mutex_lock(&client_mutex);
+       list_add(&platform->list, &platform_list);
+       snd_soc_instantiate_cards();
+       mutex_unlock(&client_mutex);
+
+       pr_debug("Registered platform '%s'\n", platform->name);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_platform);
+
+/**
+ * snd_soc_unregister_platform - Unregister a platform from the ASoC core
+ *
+ * @param platform platform to unregister
+ */
+void snd_soc_unregister_platform(struct snd_soc_platform *platform)
 {
-       printk(KERN_INFO "ASoC version %s\n", SND_SOC_VERSION);
+       mutex_lock(&client_mutex);
+       list_del(&platform->list);
+       mutex_unlock(&client_mutex);
+
+       pr_debug("Unregistered platform '%s'\n", platform->name);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);
+
+/**
+ * snd_soc_register_codec - Register a codec with the ASoC core
+ *
+ * @param codec codec to register
+ */
+int snd_soc_register_codec(struct snd_soc_codec *codec)
+{
+       if (!codec->name)
+               return -EINVAL;
+
+       /* The device should become mandatory over time */
+       if (!codec->dev)
+               printk(KERN_WARNING "No device for codec %s\n", codec->name);
+
+       INIT_LIST_HEAD(&codec->list);
+
+       mutex_lock(&client_mutex);
+       list_add(&codec->list, &codec_list);
+       snd_soc_instantiate_cards();
+       mutex_unlock(&client_mutex);
+
+       pr_debug("Registered codec '%s'\n", codec->name);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_codec);
+
+/**
+ * snd_soc_unregister_codec - Unregister a codec from the ASoC core
+ *
+ * @param codec codec to unregister
+ */
+void snd_soc_unregister_codec(struct snd_soc_codec *codec)
+{
+       mutex_lock(&client_mutex);
+       list_del(&codec->list);
+       mutex_unlock(&client_mutex);
+
+       pr_debug("Unregistered codec '%s'\n", codec->name);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);
+
+static int __init snd_soc_init(void)
+{
+#ifdef CONFIG_DEBUG_FS
+       debugfs_root = debugfs_create_dir("asoc", NULL);
+       if (IS_ERR(debugfs_root) || !debugfs_root) {
+               printk(KERN_WARNING
+                      "ASoC: Failed to create debugfs directory\n");
+               debugfs_root = NULL;
+       }
+#endif
+
        return platform_driver_register(&soc_driver);
 }
 
-static void snd_soc_exit(void)
+static void __exit snd_soc_exit(void)
 {
+#ifdef CONFIG_DEBUG_FS
+       debugfs_remove_recursive(debugfs_root);
+#endif
        platform_driver_unregister(&soc_driver);
 }
 
index 7351db9..8863edd 100644 (file)
@@ -37,7 +37,6 @@
 #include <linux/bitops.h>
 #include <linux/platform_device.h>
 #include <linux/jiffies.h>
-#include <linux/debugfs.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -67,17 +66,13 @@ static int dapm_status = 1;
 module_param(dapm_status, int, 0);
 MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
 
-static struct dentry *asoc_debugfs;
-
-static u32 pop_time;
-
-static void pop_wait(void)
+static void pop_wait(u32 pop_time)
 {
        if (pop_time)
                schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time));
 }
 
-static void pop_dbg(const char *fmt, ...)
+static void pop_dbg(u32 pop_time, const char *fmt, ...)
 {
        va_list args;
 
@@ -85,7 +80,7 @@ static void pop_dbg(const char *fmt, ...)
 
        if (pop_time) {
                vprintk(fmt, args);
-               pop_wait();
+               pop_wait(pop_time);
        }
 
        va_end(args);
@@ -230,10 +225,11 @@ static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
 
        change = old != new;
        if (change) {
-               pop_dbg("pop test %s : %s in %d ms\n", widget->name,
-                       widget->power ? "on" : "off", pop_time);
+               pop_dbg(codec->pop_time, "pop test %s : %s in %d ms\n",
+                       widget->name, widget->power ? "on" : "off",
+                       codec->pop_time);
                snd_soc_write(codec, widget->reg, new);
-               pop_wait();
+               pop_wait(codec->pop_time);
        }
        pr_debug("reg %x old %x new %x change %d\n", widget->reg,
                 old, new, change);
@@ -293,7 +289,7 @@ static int dapm_new_mixer(struct snd_soc_codec *codec,
        struct snd_soc_dapm_widget *w)
 {
        int i, ret = 0;
-       char name[32];
+       size_t name_len;
        struct snd_soc_dapm_path *path;
 
        /* add kcontrol */
@@ -307,11 +303,16 @@ static int dapm_new_mixer(struct snd_soc_codec *codec,
                                continue;
 
                        /* add dapm control with long name */
-                       snprintf(name, 32, "%s %s", w->name, w->kcontrols[i].name);
-                       path->long_name = kstrdup (name, GFP_KERNEL);
+                       name_len = 2 + strlen(w->name)
+                               + strlen(w->kcontrols[i].name);
+                       path->long_name = kmalloc(name_len, GFP_KERNEL);
                        if (path->long_name == NULL)
                                return -ENOMEM;
 
+                       snprintf(path->long_name, name_len, "%s %s",
+                                w->name, w->kcontrols[i].name);
+                       path->long_name[name_len - 1] = '\0';
+
                        path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
                                path->long_name);
                        ret = snd_ctl_add(codec->card, path->kcontrol);
@@ -821,23 +822,9 @@ static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
 
 int snd_soc_dapm_sys_add(struct device *dev)
 {
-       int ret = 0;
-
        if (!dapm_status)
                return 0;
-
-       ret = device_create_file(dev, &dev_attr_dapm_widget);
-       if (ret != 0)
-               return ret;
-
-       asoc_debugfs = debugfs_create_dir("asoc", NULL);
-       if (!IS_ERR(asoc_debugfs) && asoc_debugfs)
-               debugfs_create_u32("dapm_pop_time", 0744, asoc_debugfs,
-                                  &pop_time);
-       else
-               asoc_debugfs = NULL;
-
-       return 0;
+       return device_create_file(dev, &dev_attr_dapm_widget);
 }
 
 static void snd_soc_dapm_sys_remove(struct device *dev)
@@ -845,9 +832,6 @@ static void snd_soc_dapm_sys_remove(struct device *dev)
        if (dapm_status) {
                device_remove_file(dev, &dev_attr_dapm_widget);
        }
-
-       if (asoc_debugfs)
-               debugfs_remove_recursive(asoc_debugfs);
 }
 
 /* free all dapm widgets and resources */
@@ -1006,28 +990,6 @@ err:
        return ret;
 }
 
-/**
- * snd_soc_dapm_connect_input - connect dapm widgets
- * @codec: audio codec
- * @sink: name of target widget
- * @control: mixer control name
- * @source: name of source name
- *
- * Connects 2 dapm widgets together via a named audio path. The sink is
- * the widget receiving the audio signal, whilst the source is the sender
- * of the audio signal.
- *
- * This function has been deprecated in favour of snd_soc_dapm_add_routes().
- *
- * Returns 0 for success else error.
- */
-int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
-       const char *control, const char *source)
-{
-       return snd_soc_dapm_add_route(codec, sink, control, source);
-}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input);
-
 /**
  * snd_soc_dapm_add_routes - Add routes between DAPM widgets
  * @codec: codec
@@ -1358,8 +1320,12 @@ int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
 
        for (i = 0; i < num; i++) {
                ret = snd_soc_dapm_new_control(codec, widget);
-               if (ret < 0)
+               if (ret < 0) {
+                       printk(KERN_ERR
+                              "ASoC: Failed to create DAPM control %s: %d\n",
+                              widget->name, ret);
                        return ret;
+               }
                widget++;
        }
        return 0;
@@ -1440,11 +1406,11 @@ int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
                                enum snd_soc_bias_level level)
 {
        struct snd_soc_codec *codec = socdev->codec;
-       struct snd_soc_machine *machine = socdev->machine;
+       struct snd_soc_card *card = socdev->card;
        int ret = 0;
 
-       if (machine->set_bias_level)
-               ret = machine->set_bias_level(machine, level);
+       if (card->set_bias_level)
+               ret = card->set_bias_level(card, level);
        if (ret == 0 && codec->set_bias_level)
                ret = codec->set_bias_level(codec, level);
 
index 10ba421..2b302bb 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/module.h>
 #include <linux/device.h>
 #include <linux/err.h>
+#include <sound/core.h>
 
 #ifdef CONFIG_SOUND_OSS_CORE
 static int __init init_oss_soundcore(void);
index 798ca12..ccd763d 100644 (file)
@@ -247,69 +247,56 @@ static struct caiaq_controller a8dj_controller[] = {
        { "Software lock",                      40              }
 };
 
-int __devinit snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev)
+static int __devinit add_controls(struct caiaq_controller *c, int num,
+                                 struct snd_usb_caiaqdev *dev)
 {
-       int i;
+       int i, ret;
        struct snd_kcontrol *kc;
 
+       for (i = 0; i < num; i++, c++) {
+               kcontrol_template.name = c->name;
+               kcontrol_template.private_value = c->index;
+               kc = snd_ctl_new1(&kcontrol_template, dev);
+               ret = snd_ctl_add(dev->chip.card, kc);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+int __devinit snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev)
+{
+       int ret = 0;
+
        switch (dev->chip.usb_id) {
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
-               for (i = 0; i < ARRAY_SIZE(ak1_controller); i++) {
-                       struct caiaq_controller *c = ak1_controller + i;
-                       kcontrol_template.name = c->name;
-                       kcontrol_template.private_value = c->index;
-                       kc = snd_ctl_new1(&kcontrol_template, dev);
-                       snd_ctl_add(dev->chip.card, kc);
-               }
-
+               ret = add_controls(ak1_controller,
+                       ARRAY_SIZE(ak1_controller), dev);
                break;
 
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
-               for (i = 0; i < ARRAY_SIZE(rk2_controller); i++) {
-                       struct caiaq_controller *c = rk2_controller + i;
-                       kcontrol_template.name = c->name;
-                       kcontrol_template.private_value = c->index;
-                       kc = snd_ctl_new1(&kcontrol_template, dev);
-                       snd_ctl_add(dev->chip.card, kc);
-               }
-
+               ret = add_controls(rk2_controller,
+                       ARRAY_SIZE(rk2_controller), dev);
                break;
 
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
-               for (i = 0; i < ARRAY_SIZE(rk3_controller); i++) {
-                       struct caiaq_controller *c = rk3_controller + i;
-                       kcontrol_template.name = c->name;
-                       kcontrol_template.private_value = c->index;
-                       kc = snd_ctl_new1(&kcontrol_template, dev);
-                       snd_ctl_add(dev->chip.card, kc);
-               }
-
+               ret = add_controls(rk3_controller,
+                       ARRAY_SIZE(rk3_controller), dev);
                break;
 
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
-               for (i = 0; i < ARRAY_SIZE(kore_controller); i++) {
-                       struct caiaq_controller *c = kore_controller + i;
-                       kcontrol_template.name = c->name;
-                       kcontrol_template.private_value = c->index;
-                       kc = snd_ctl_new1(&kcontrol_template, dev);
-                       snd_ctl_add(dev->chip.card, kc);
-               }
-
+               ret = add_controls(kore_controller,
+                       ARRAY_SIZE(kore_controller), dev);
                break;
 
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
-               for (i = 0; i < ARRAY_SIZE(a8dj_controller); i++) {
-                       struct caiaq_controller *c = a8dj_controller + i;
-                       kcontrol_template.name = c->name;
-                       kcontrol_template.private_value = c->index;
-                       kc = snd_ctl_new1(&kcontrol_template, dev);
-                       snd_ctl_add(dev->chip.card, kc);
-               }
-
+               ret = add_controls(a8dj_controller,
+                       ARRAY_SIZE(a8dj_controller), dev);
                break;
        }
 
-       return 0;
+       return ret;
 }
 
index 8317508..b143ef7 100644 (file)
@@ -42,7 +42,7 @@
 #endif
 
 MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
-MODULE_DESCRIPTION("caiaq USB audio, version 1.3.8");
+MODULE_DESCRIPTION("caiaq USB audio, version 1.3.9");
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
                         "{Native Instruments, RigKontrol3},"
index 5962e4b..6d9f9b1 100644 (file)
@@ -880,7 +880,7 @@ static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream,
                                snd_rawmidi_transmit_ack(substream, 1);
                        return;
                }
-               tasklet_hi_schedule(&port->ep->tasklet);
+               tasklet_schedule(&port->ep->tasklet);
        }
 }
 
index ff23cc1..70b9635 100644 (file)
@@ -276,7 +276,8 @@ static void subs_set_complete(struct urb **urbs, void (*complete)(struct urb *))
        }
 }
 
-int usb_stream_prepare_playback(struct usb_stream_kernel *sk, struct urb *inurb)
+static int usb_stream_prepare_playback(struct usb_stream_kernel *sk,
+               struct urb *inurb)
 {
        struct usb_stream *s = sk->s;
        struct urb *io;