Merge branch 'android-exynos-3.4' into android-exynos-manta-3.4
diff --git a/Documentation/prctl/seccomp_filter.txt b/Documentation/prctl/seccomp_filter.txt
new file mode 100644
index 0000000..597c3c5
--- /dev/null
+++ b/Documentation/prctl/seccomp_filter.txt
@@ -0,0 +1,163 @@
+		SECure COMPuting with filters
+		=============================
+
+Introduction
+------------
+
+A large number of system calls are exposed to every userland process
+with many of them going unused for the entire lifetime of the process.
+As system calls change and mature, bugs are found and eradicated.  A
+certain subset of userland applications benefit by having a reduced set
+of available system calls.  The resulting set reduces the total kernel
+surface exposed to the application.  System call filtering is meant for
+use with those applications.
+
+Seccomp filtering provides a means for a process to specify a filter for
+incoming system calls.  The filter is expressed as a Berkeley Packet
+Filter (BPF) program, as with socket filters, except that the data
+operated on is related to the system call being made: system call
+number and the system call arguments.  This allows for expressive
+filtering of system calls using a filter program language with a long
+history of being exposed to userland and a straightforward data set.
+
+Additionally, BPF makes it impossible for users of seccomp to fall prey
+to time-of-check-time-of-use (TOCTOU) attacks that are common in system
+call interposition frameworks.  BPF programs may not dereference
+pointers which constrains all filters to solely evaluating the system
+call arguments directly.
+
+What it isn't
+-------------
+
+System call filtering isn't a sandbox.  It provides a clearly defined
+mechanism for minimizing the exposed kernel surface.  It is meant to be
+a tool for sandbox developers to use.  Beyond that, policy for logical
+behavior and information flow should be managed with a combination of
+other system hardening techniques and, potentially, an LSM of your
+choosing.  Expressive, dynamic filters provide further options down this
+path (avoiding pathological sizes or selecting which of the multiplexed
+system calls in socketcall() is allowed, for instance) which could be
+construed, incorrectly, as a more complete sandboxing solution.
+
+Usage
+-----
+
+An additional seccomp mode is added and is enabled using the same
+prctl(2) call as the strict seccomp.  If the architecture has
+CONFIG_HAVE_ARCH_SECCOMP_FILTER, then filters may be added as below:
+
+PR_SET_SECCOMP:
+	Now takes an additional argument which specifies a new filter
+	using a BPF program.
+	The BPF program will be executed over struct seccomp_data
+	reflecting the system call number, arguments, and other
+	metadata.  The BPF program must then return one of the
+	acceptable values to inform the kernel which action should be
+	taken.
+
+	Usage:
+		prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, prog);
+
+	The 'prog' argument is a pointer to a struct sock_fprog which
+	will contain the filter program.  If the program is invalid, the
+	call will return -1 and set errno to EINVAL.
+
+	If fork/clone and execve are allowed by @prog, any child
+	processes will be constrained to the same filters and system
+	call ABI as the parent.
+
+	Prior to use, the task must call prctl(PR_SET_NO_NEW_PRIVS, 1) or
+	run with CAP_SYS_ADMIN privileges in its namespace.  If these are not
+	true, -EACCES will be returned.  This requirement ensures that filter
+	programs cannot be applied to child processes with greater privileges
+	than the task that installed them.
+
+	Additionally, if prctl(2) is allowed by the attached filter,
+	additional filters may be layered on which will increase evaluation
+	time, but allow for further decreasing the attack surface during
+	execution of a process.
+
+The above call returns 0 on success and non-zero on error.
+
+Return values
+-------------
+A seccomp filter may return any of the following values. If multiple
+filters exist, the return value for the evaluation of a given system
+call will always use the highest precedent value. (For example,
+SECCOMP_RET_KILL will always take precedence.)
+
+In precedence order, they are:
+
+SECCOMP_RET_KILL:
+	Results in the task exiting immediately without executing the
+	system call.  The exit status of the task (status & 0x7f) will
+	be SIGSYS, not SIGKILL.
+
+SECCOMP_RET_TRAP:
+	Results in the kernel sending a SIGSYS signal to the triggering
+	task without executing the system call.  The kernel will
+	rollback the register state to just before the system call
+	entry such that a signal handler in the task will be able to
+	inspect the ucontext_t->uc_mcontext registers and emulate
+	system call success or failure upon return from the signal
+	handler.
+
+	The SECCOMP_RET_DATA portion of the return value will be passed
+	as si_errno.
+
+	SIGSYS triggered by seccomp will have a si_code of SYS_SECCOMP.
+
+SECCOMP_RET_ERRNO:
+	Results in the lower 16-bits of the return value being passed
+	to userland as the errno without executing the system call.
+
+SECCOMP_RET_TRACE:
+	When returned, this value will cause the kernel to attempt to
+	notify a ptrace()-based tracer prior to executing the system
+	call.  If there is no tracer present, -ENOSYS is returned to
+	userland and the system call is not executed.
+
+	A tracer will be notified if it requests PTRACE_O_TRACESECCOMP
+	using ptrace(PTRACE_SETOPTIONS).  The tracer will be notified
+	of a PTRACE_EVENT_SECCOMP and the SECCOMP_RET_DATA portion of
+	the BPF program return value will be available to the tracer
+	via PTRACE_GETEVENTMSG.
+
+SECCOMP_RET_ALLOW:
+	Results in the system call being executed.
+
+If multiple filters exist, the return value for the evaluation of a
+given system call will always use the highest precedent value.
+
+Precedence is only determined using the SECCOMP_RET_ACTION mask.  When
+multiple filters return values of the same precedence, only the
+SECCOMP_RET_DATA from the most recently installed filter will be
+returned.
+
+Pitfalls
+--------
+
+The biggest pitfall to avoid during use is filtering on system call
+number without checking the architecture value.  Why?  On any
+architecture that supports multiple system call invocation conventions,
+the system call numbers may vary based on the specific invocation.  If
+the numbers in the different calling conventions overlap, then checks in
+the filters may be abused.  Always check the arch value!
+
+Example
+-------
+
+The samples/seccomp/ directory contains both an x86-specific example
+and a more generic example of a higher level macro interface for BPF
+program generation.
+
+
+
+Adding architecture support
+-----------------------
+
+See arch/Kconfig for the authoritative requirements.  In general, if an
+architecture supports both ptrace_event and seccomp, it will be able to
+support seccomp filter with minor fixup: SIGSYS support and seccomp return
+value checking.  Then it must just add CONFIG_HAVE_ARCH_SECCOMP_FILTER
+to its arch-specific Kconfig.
diff --git a/arch/Kconfig b/arch/Kconfig
index 684eb5a..c024b3e 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -216,4 +216,27 @@
 config ARCH_WANT_OLD_COMPAT_IPC
 	bool
 
+config HAVE_ARCH_SECCOMP_FILTER
+	bool
+	help
+	  An arch should select this symbol if it provides all of these things:
+	  - syscall_get_arch()
+	  - syscall_get_arguments()
+	  - syscall_rollback()
+	  - syscall_set_return_value()
+	  - SIGSYS siginfo_t support
+	  - secure_computing is called from a ptrace_event()-safe context
+	  - secure_computing return value is checked and a return value of -1
+	    results in the system call being skipped immediately.
+
+config SECCOMP_FILTER
+	def_bool y
+	depends on HAVE_ARCH_SECCOMP_FILTER && SECCOMP && NET
+	help
+	  Enable tasks to build secure computing environments defined
+	  in terms of Berkeley Packet Filter programs which implement
+	  task-defined system call filtering polices.
+
+	  See Documentation/prctl/seccomp_filter.txt for details.
+
 source "kernel/gcov/Kconfig"
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 3a07bf8..4e30467 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -30,6 +30,8 @@
 	select HAVE_HW_BREAKPOINT if (PERF_EVENTS && (CPU_V6 || CPU_V6K || CPU_V7))
 	select HAVE_C_RECORDMCOUNT
 	select HAVE_GENERIC_HARDIRQS
+	select HAVE_SPARSE_IRQ
+	select HAVE_ARCH_SECCOMP_FILTER
 	select GENERIC_IRQ_SHOW
 	select CPU_PM if (SUSPEND || CPU_IDLE)
 	select GENERIC_PCI_IOMAP
@@ -1445,6 +1447,16 @@
 	  affected by this erratum such that no streaming-write ever allocates
 	  into the L2 cache.
 
+config ARM_ERRATA_798181
+	bool "ARM errata: TLBI/DSB failure on Cortex-A15"
+	depends on CPU_V7 && SMP
+	help
+	  On Cortex-A15 (r0p0..r3p2) the TLBI*IS/DSB operations are not
+	  adequately shooting down all use of the old entries. This
+	  option enables the Linux kernel workaround for this erratum
+	  which sends an IPI to the CPUs that are running the same ASID
+	  as the one being invalidated.
+
 endmenu
 
 source "arch/arm/common/Kconfig"
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 037bd5a..bfd179f 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -353,4 +353,13 @@
 	help
 	  Perform tests of kprobes API and instruction set simulation.
 
+config PID_IN_CONTEXTIDR
+	bool "Write the current PID to the CONTEXTIDR register"
+	depends on CPU_COPY_V6
+	help
+	  Enabling this option causes the kernel to write the current PID to
+	  the PROCID field of the CONTEXTIDR register, at the expense of some
+	  additional instructions during context switch. Say Y here only if you
+	  are planning to use hardware trace tools with this kernel.
+
 endmenu
diff --git a/arch/arm/configs/manta_defconfig b/arch/arm/configs/manta_defconfig
new file mode 100644
index 0000000..20deb27
--- /dev/null
+++ b/arch/arm/configs/manta_defconfig
@@ -0,0 +1,466 @@
+CONFIG_EXPERIMENTAL=y
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_AUDIT=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_KALLSYMS_ALL=y
+# CONFIG_AIO is not set
+CONFIG_EMBEDDED=y
+CONFIG_PERF_EVENTS=y
+CONFIG_DEBUG_PERF_USE_VMALLOC=y
+CONFIG_MODULES=y
+CONFIG_MODULE_FORCE_LOAD=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
+CONFIG_ARCH_EXYNOS=y
+CONFIG_S3C_LOWLEVEL_UART_PORT=2
+CONFIG_S3C_ADC=y
+CONFIG_S3C24XX_PWM=y
+CONFIG_EXYNOS_CONTENT_PATH_PROTECTION=y
+CONFIG_EXYNOS5_CORESIGHT=y
+CONFIG_EXYNOS_THERMAL=y
+CONFIG_MACH_SMDK5250=y
+CONFIG_MACH_MANTA=y
+CONFIG_ARM_TRUSTZONE=y
+CONFIG_ARM_ERRATA_798181=y
+CONFIG_FIQ_DEBUGGER=y
+CONFIG_FIQ_DEBUGGER_NO_SLEEP=y
+CONFIG_FIQ_DEBUGGER_CONSOLE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+CONFIG_NR_CPUS=2
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_COMPACTION=y
+CONFIG_SECCOMP=y
+CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y
+CONFIG_CMDLINE="vmalloc=512M debug_core.break_on_panic=0 debug_core.break_on_exception=0 no_console_suspend s3c2410-wdt.tmr_atboot=1 s3c2410-wdt.tmr_margin=30 wire.search_count=5"
+CONFIG_CMDLINE_EXTEND=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_BINFMT_MISC=y
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
+CONFIG_PM_RUNTIME=y
+CONFIG_PM_DEBUG=y
+CONFIG_PM_OPP=y
+CONFIG_SUSPEND_TIME=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_TUNNEL=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_LOG=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_TARGET_REJECT_SKERR=y
+CONFIG_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_SECURITY=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_TARGET_REJECT_SKERR=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_PHONET=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_BT=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_HIDP=y
+CONFIG_BT_HCIUART=y
+CONFIG_BT_HCIUART_H4=y
+CONFIG_CFG80211=y
+CONFIG_NL80211_TESTMODE=y
+# CONFIG_CFG80211_DEFAULT_PS is not set
+# CONFIG_CFG80211_WEXT is not set
+CONFIG_RFKILL=y
+CONFIG_NFC_DEVICES=y
+CONFIG_BCM2079X_NFC_SPI=y
+CONFIG_SYNC=y
+CONFIG_SW_SYNC=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_HAPTIC_ISA1200=y
+CONFIG_UID_STAT=y
+CONFIG_STMPE811_ADC=y
+CONFIG_AUDIENCE_ES305=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_NETDEVICES=y
+CONFIG_TUN=y
+# CONFIG_ETHERNET is not set
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_USB_USBNET=y
+CONFIG_WIFI_CONTROL_FUNC=y
+CONFIG_BCMDHD=y
+CONFIG_BCMDHD_FW_PATH="/system/vendor/firmware/fw_bcmdhd.bin"
+CONFIG_DHD_USE_SCHED_SCAN=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_XPAD=y
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
+CONFIG_INPUT_TABLET=y
+CONFIG_TABLET_USB_ACECAD=y
+CONFIG_TABLET_USB_AIPTEK=y
+CONFIG_TABLET_USB_GTCO=y
+CONFIG_TABLET_USB_HANWANG=y
+CONFIG_TABLET_USB_KBTAB=y
+CONFIG_TABLET_USB_WACOM=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_TOUCHSCREEN_EGALAX_I2C=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_SAMSUNG=y
+CONFIG_SERIAL_SAMSUNG_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_GPIO=y
+CONFIG_I2C_S3C2410=y
+CONFIG_SPI=y
+CONFIG_SPI_S3C64XX=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_W1_MASTER_DS2482=y
+CONFIG_FUELGAUGE_DS2784=y
+CONFIG_BATTERY_ANDROID=y
+CONFIG_CHARGER_SMB347=y
+# CONFIG_HWMON is not set
+CONFIG_WATCHDOG=y
+CONFIG_S3C2410_WATCHDOG=y
+CONFIG_MFD_MAX77686=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_MAX77686=y
+CONFIG_REGULATOR_WM8994=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+# CONFIG_RC_CORE is not set
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2063 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_TUNER_XC4000 is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+# CONFIG_MEDIA_TUNER_TDA18212 is not set
+CONFIG_VIDEO_S5K4E5=y
+CONFIG_VIDEO_S5K6A3=y
+CONFIG_VIDEO_EXYNOS=y
+CONFIG_VIDEO_EXYNOS_GSCALER=y
+CONFIG_VIDEO_EXYNOS_JPEG=y
+CONFIG_VIDEO_EXYNOS_FIMG2D=y
+CONFIG_VIDEO_EXYNOS_MFC=y
+CONFIG_VIDEO_EXYNOS_TV=y
+CONFIG_VIDEO_EXYNOS_HDCP=y
+CONFIG_VIDEO_EXYNOS_ROTATOR=y
+CONFIG_VIDEO_EXYNOS5_FIMC_IS2=y
+CONFIG_ION=y
+CONFIG_ION_EXYNOS=y
+CONFIG_ION_EXYNOS_CONTIGHEAP_SIZE=2048
+CONFIG_MALI_T6XX=y
+CONFIG_MALI_T6XX_DVFS=y
+CONFIG_MALI_T6XX_RT_PM=y
+CONFIG_MALI_T6XX_DEBUG_SYS=y
+CONFIG_MALI_EXPERT=y
+CONFIG_MALI_PLATFORM_FAKE=y
+CONFIG_MALI_PLATFORM_THIRDPARTY_NAME="manta"
+CONFIG_FB=y
+CONFIG_FB_S3C=y
+CONFIG_S5P_DP=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_LCD_PLATFORM=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_BACKLIGHT_PWM=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+# CONFIG_SND_DRIVERS is not set
+# CONFIG_SND_ARM is not set
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_SAMSUNG=y
+CONFIG_SND_SOC_SAMSUNG_MANTA_WM1811=y
+CONFIG_SND_SOC_SAMSUNG_MANTA_SPDIF=y
+CONFIG_UHID=y
+CONFIG_USB_HIDDEV=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_ACRUX=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_PRODIKEYS=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DRAGONRISE=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_HOLTEK=y
+CONFIG_HID_KEYTOUCH=y
+CONFIG_HID_KYE=y
+CONFIG_HID_UCLOGIC=y
+CONFIG_HID_WALTOP=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LCPOWER=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_HID_NTRIG=y
+CONFIG_HID_ORTEK=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_PICOLCD=y
+CONFIG_HID_PRIMAX=y
+CONFIG_HID_ROCCAT=y
+CONFIG_HID_SAITEK=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SPEEDLINK=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_GREENASIA=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_HID_TIVO=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_HID_WACOM=y
+CONFIG_HID_WIIMOTE=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_HID_ZYDACRON=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_S5P=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_EXYNOS=y
+CONFIG_USB_PRINTER=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_S3C_OTGD=y
+CONFIG_USB_G_ANDROID=y
+CONFIG_USB_ANDROID_RNDIS_DWORD_ALIGNED=y
+CONFIG_USB_OTG_WAKELOCK=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_S3C=y
+CONFIG_MMC_SDHCI_S3C_DMA=y
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_IDMAC=y
+CONFIG_LEDS_AS3668=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_SWITCH=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_MAX77686=y
+CONFIG_STAGING=y
+CONFIG_IIO=y
+CONFIG_IIO_BUFFER=y
+CONFIG_IIO_KFIFO_BUF=y
+CONFIG_INV_MPU_IIO=y
+CONFIG_SENSORS_BH1721=y
+CONFIG_BMP182=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ANDROID_INTF_ALARM_DEV=y
+CONFIG_EXYNOS_IOMMU=y
+CONFIG_PM_DEVFREQ=y
+CONFIG_DEVFREQ_GOV_PM_QOS=y
+CONFIG_ARM_EXYNOS5_BUS_DEVFREQ=y
+CONFIG_MOBICORE_DRIVER=y
+CONFIG_MOBICORE_API=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+# CONFIG_DNOTIFY is not set
+CONFIG_FUSE_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+# CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=10
+CONFIG_SCHEDSTATS=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+CONFIG_DEBUG_INFO=y
+CONFIG_ENABLE_DEFAULT_TRACERS=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_KGDB=y
+CONFIG_KGDB_KDB=y
+# CONFIG_ARM_UNWIND is not set
+CONFIG_DEBUG_USER=y
+CONFIG_DEBUG_RODATA=y
+CONFIG_DEBUG_RODATA_TEST=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_LSM_MMAP_MIN_ADDR=4096
+CONFIG_SECURITY_SELINUX=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRC_CCITT=y
diff --git a/arch/arm/include/asm/highmem.h b/arch/arm/include/asm/highmem.h
index 8c5e828..91b99ab 100644
--- a/arch/arm/include/asm/highmem.h
+++ b/arch/arm/include/asm/highmem.h
@@ -41,6 +41,13 @@
 #endif
 #endif
 
+/*
+ * Needed to be able to broadcast the TLB invalidation for kmap.
+ */
+#ifdef CONFIG_ARM_ERRATA_798181
+#undef ARCH_NEEDS_KMAP_HIGH_GET
+#endif
+
 #ifdef ARCH_NEEDS_KMAP_HIGH_GET
 extern void *kmap_high_get(struct page *page);
 #else
diff --git a/arch/arm/include/asm/mmu.h b/arch/arm/include/asm/mmu.h
index 1496565..e3d5554 100644
--- a/arch/arm/include/asm/mmu.h
+++ b/arch/arm/include/asm/mmu.h
@@ -5,18 +5,15 @@
 
 typedef struct {
 #ifdef CONFIG_CPU_HAS_ASID
-	unsigned int id;
-	raw_spinlock_t id_lock;
+	atomic64_t	id;
 #endif
-	unsigned int kvm_seq;
+	unsigned int	vmalloc_seq;
 } mm_context_t;
 
 #ifdef CONFIG_CPU_HAS_ASID
-#define ASID(mm)	((mm)->context.id & 255)
-
-/* init_mm.context.id_lock should be initialized. */
-#define INIT_MM_CONTEXT(name)                                                 \
-	.context.id_lock    = __RAW_SPIN_LOCK_UNLOCKED(name.context.id_lock),
+#define ASID_BITS	8
+#define ASID_MASK	((~0ULL) << ASID_BITS)
+#define ASID(mm)	((mm)->context.id.counter & ~ASID_MASK)
 #else
 #define ASID(mm)	(0)
 #endif
@@ -29,7 +26,7 @@
  *  modified for 2.6 by Hyok S. Choi <hyok.choi@samsung.com>
  */
 typedef struct {
-	unsigned long		end_brk;
+	unsigned long	end_brk;
 } mm_context_t;
 
 #endif
diff --git a/arch/arm/include/asm/mmu_context.h b/arch/arm/include/asm/mmu_context.h
index 0306bc6..a7b85e0 100644
--- a/arch/arm/include/asm/mmu_context.h
+++ b/arch/arm/include/asm/mmu_context.h
@@ -20,88 +20,14 @@
 #include <asm/proc-fns.h>
 #include <asm-generic/mm_hooks.h>
 
-void __check_kvm_seq(struct mm_struct *mm);
+void __check_vmalloc_seq(struct mm_struct *mm);
 
 #ifdef CONFIG_CPU_HAS_ASID
 
-/*
- * On ARMv6, we have the following structure in the Context ID:
- *
- * 31                         7          0
- * +-------------------------+-----------+
- * |      process ID         |   ASID    |
- * +-------------------------+-----------+
- * |              context ID             |
- * +-------------------------------------+
- *
- * The ASID is used to tag entries in the CPU caches and TLBs.
- * The context ID is used by debuggers and trace logic, and
- * should be unique within all running processes.
- */
-#define ASID_BITS		8
-#define ASID_MASK		((~0) << ASID_BITS)
-#define ASID_FIRST_VERSION	(1 << ASID_BITS)
+void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk);
+#define init_new_context(tsk,mm)	({ atomic64_set(&mm->context.id, 0); 0; })
 
-extern unsigned int cpu_last_asid;
-
-void __init_new_context(struct task_struct *tsk, struct mm_struct *mm);
-void __new_context(struct mm_struct *mm);
-void cpu_set_reserved_ttbr0(void);
-
-static inline void switch_new_context(struct mm_struct *mm)
-{
-	unsigned long flags;
-
-	__new_context(mm);
-
-	local_irq_save(flags);
-	cpu_switch_mm(mm->pgd, mm);
-	local_irq_restore(flags);
-}
-
-static inline void check_and_switch_context(struct mm_struct *mm,
-					    struct task_struct *tsk)
-{
-	if (unlikely(mm->context.kvm_seq != init_mm.context.kvm_seq))
-		__check_kvm_seq(mm);
-
-	/*
-	 * Required during context switch to avoid speculative page table
-	 * walking with the wrong TTBR.
-	 */
-	cpu_set_reserved_ttbr0();
-
-	if (!((mm->context.id ^ cpu_last_asid) >> ASID_BITS))
-		/*
-		 * The ASID is from the current generation, just switch to the
-		 * new pgd. This condition is only true for calls from
-		 * context_switch() and interrupts are already disabled.
-		 */
-		cpu_switch_mm(mm->pgd, mm);
-	else if (irqs_disabled())
-		/*
-		 * Defer the new ASID allocation until after the context
-		 * switch critical region since __new_context() cannot be
-		 * called with interrupts disabled (it sends IPIs).
-		 */
-		set_ti_thread_flag(task_thread_info(tsk), TIF_SWITCH_MM);
-	else
-		/*
-		 * That is a direct call to switch_mm() or activate_mm() with
-		 * interrupts enabled and a new context.
-		 */
-		switch_new_context(mm);
-}
-
-#define init_new_context(tsk,mm)	(__init_new_context(tsk,mm),0)
-
-#define finish_arch_post_lock_switch \
-	finish_arch_post_lock_switch
-static inline void finish_arch_post_lock_switch(void)
-{
-	if (test_and_clear_thread_flag(TIF_SWITCH_MM))
-		switch_new_context(current->mm);
-}
+DECLARE_PER_CPU(atomic64_t, active_asids);
 
 #else	/* !CONFIG_CPU_HAS_ASID */
 
@@ -110,8 +36,8 @@
 static inline void check_and_switch_context(struct mm_struct *mm,
 					    struct task_struct *tsk)
 {
-	if (unlikely(mm->context.kvm_seq != init_mm.context.kvm_seq))
-		__check_kvm_seq(mm);
+	if (unlikely(mm->context.vmalloc_seq != init_mm.context.vmalloc_seq))
+		__check_vmalloc_seq(mm);
 
 	if (irqs_disabled())
 		/*
@@ -143,6 +69,7 @@
 #endif	/* CONFIG_CPU_HAS_ASID */
 
 #define destroy_context(mm)		do { } while(0)
+#define activate_mm(prev,next)		switch_mm(prev, next, NULL)
 
 /*
  * This is called when "tsk" is about to enter lazy TLB mode.
@@ -186,6 +113,5 @@
 }
 
 #define deactivate_mm(tsk,mm)	do { } while (0)
-#define activate_mm(prev,next)	switch_mm(prev, next, NULL)
 
 #endif
diff --git a/arch/arm/include/asm/syscall.h b/arch/arm/include/asm/syscall.h
new file mode 100644
index 0000000..4c123db
--- /dev/null
+++ b/arch/arm/include/asm/syscall.h
@@ -0,0 +1,80 @@
+/*
+ * Access to user system call parameters and results
+ *
+ * Copyright (C) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org>
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * See asm-generic/syscall.h for descriptions of what we must do here.
+ */
+#ifndef _ASM_ARM_SYSCALL_H
+#define _ASM_ARM_SYSCALL_H
+
+#include <linux/audit.h> /* for AUDIT_ARCH_* */
+#include <linux/elf.h> /* for ELF_EM */
+#include <linux/sched.h>
+#include <linux/thread_info.h> /* for task_thread_info */
+#include <linux/err.h>
+
+static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs)
+{
+	return task_thread_info(task)->syscall;
+}
+
+static inline void syscall_rollback(struct task_struct *task,
+				    struct pt_regs *regs)
+{
+	regs->ARM_r0 = regs->ARM_ORIG_r0;
+}
+
+static inline long syscall_get_error(struct task_struct *task,
+				     struct pt_regs *regs)
+{
+	unsigned long error = regs->ARM_r0;
+	return IS_ERR_VALUE(error) ? error : 0;
+}
+
+static inline long syscall_get_return_value(struct task_struct *task,
+					    struct pt_regs *regs)
+{
+	return regs->ARM_r0;
+}
+
+static inline void syscall_set_return_value(struct task_struct *task,
+					    struct pt_regs *regs,
+					    int error, long val)
+{
+	regs->ARM_r0 = (long) error ?: 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->ARM_r0 + 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->ARM_r0 + i, args, n * sizeof(args[0]));
+}
+
+static inline int syscall_get_arch(struct task_struct *task,
+				    struct pt_regs *regs)
+{
+	/* ARM tasks don't change audit architectures on the fly. */
+#ifdef __ARMEB__
+	return AUDIT_ARCH_ARMEB;
+#else
+	return AUDIT_ARCH_ARM;
+#endif
+}
+#endif	/* _ASM_ARM_SYSCALL_H */
diff --git a/arch/arm/include/asm/tlbflush.h b/arch/arm/include/asm/tlbflush.h
index 85fe61e..9e9c041 100644
--- a/arch/arm/include/asm/tlbflush.h
+++ b/arch/arm/include/asm/tlbflush.h
@@ -34,10 +34,13 @@
 #define TLB_V6_D_ASID	(1 << 17)
 #define TLB_V6_I_ASID	(1 << 18)
 
+#define TLB_V6_BP	(1 << 19)
+
 /* Unified Inner Shareable TLB operations (ARMv7 MP extensions) */
-#define TLB_V7_UIS_PAGE	(1 << 19)
-#define TLB_V7_UIS_FULL (1 << 20)
-#define TLB_V7_UIS_ASID (1 << 21)
+#define TLB_V7_UIS_PAGE	(1 << 20)
+#define TLB_V7_UIS_FULL (1 << 21)
+#define TLB_V7_UIS_ASID (1 << 22)
+#define TLB_V7_UIS_BP	(1 << 23)
 
 #define TLB_BARRIER	(1 << 28)
 #define TLB_L2CLEAN_FR	(1 << 29)		/* Feroceon */
@@ -65,21 +68,6 @@
 #define MULTI_TLB 1
 #endif
 
-#define v3_tlb_flags	(TLB_V3_FULL | TLB_V3_PAGE)
-
-#ifdef CONFIG_CPU_TLB_V3
-# define v3_possible_flags	v3_tlb_flags
-# define v3_always_flags	v3_tlb_flags
-# ifdef _TLB
-#  define MULTI_TLB 1
-# else
-#  define _TLB v3
-# endif
-#else
-# define v3_possible_flags	0
-# define v3_always_flags	(-1UL)
-#endif
-
 #define v4_tlb_flags	(TLB_V4_U_FULL | TLB_V4_U_PAGE)
 
 #ifdef CONFIG_CPU_TLB_V4WT
@@ -165,7 +153,8 @@
 #define v6wbi_tlb_flags (TLB_WB | TLB_DCLEAN | TLB_BARRIER | \
 			 TLB_V6_I_FULL | TLB_V6_D_FULL | \
 			 TLB_V6_I_PAGE | TLB_V6_D_PAGE | \
-			 TLB_V6_I_ASID | TLB_V6_D_ASID)
+			 TLB_V6_I_ASID | TLB_V6_D_ASID | \
+			 TLB_V6_BP)
 
 #ifdef CONFIG_CPU_TLB_V6
 # define v6wbi_possible_flags	v6wbi_tlb_flags
@@ -181,9 +170,11 @@
 #endif
 
 #define v7wbi_tlb_flags_smp	(TLB_WB | TLB_DCLEAN | TLB_BARRIER | \
-			 TLB_V7_UIS_FULL | TLB_V7_UIS_PAGE | TLB_V7_UIS_ASID)
+				 TLB_V7_UIS_FULL | TLB_V7_UIS_PAGE | \
+				 TLB_V7_UIS_ASID | TLB_V7_UIS_BP)
 #define v7wbi_tlb_flags_up	(TLB_WB | TLB_DCLEAN | TLB_BARRIER | \
-			 TLB_V6_U_FULL | TLB_V6_U_PAGE | TLB_V6_U_ASID)
+				 TLB_V6_U_FULL | TLB_V6_U_PAGE | \
+				 TLB_V6_U_ASID | TLB_V6_BP)
 
 #ifdef CONFIG_CPU_TLB_V7
 
@@ -298,8 +289,7 @@
  * implemented the "%?" method, but this has been discontinued due to too
  * many people getting it wrong.
  */
-#define possible_tlb_flags	(v3_possible_flags | \
-				 v4_possible_flags | \
+#define possible_tlb_flags	(v4_possible_flags | \
 				 v4wbi_possible_flags | \
 				 fr_possible_flags | \
 				 v4wb_possible_flags | \
@@ -307,8 +297,7 @@
 				 v6wbi_possible_flags | \
 				 v7wbi_possible_flags)
 
-#define always_tlb_flags	(v3_always_flags & \
-				 v4_always_flags & \
+#define always_tlb_flags	(v4_always_flags & \
 				 v4wbi_always_flags & \
 				 fr_always_flags & \
 				 v4wb_always_flags & \
@@ -447,6 +436,35 @@
 	}
 }
 
+static inline void local_flush_bp_all(void)
+{
+	const int zero = 0;
+	const unsigned int __tlb_flag = __cpu_tlb_flags;
+
+	if (tlb_flag(TLB_V7_UIS_BP))
+		asm("mcr p15, 0, %0, c7, c1, 6" : : "r" (zero));
+	else if (tlb_flag(TLB_V6_BP))
+		asm("mcr p15, 0, %0, c7, c5, 6" : : "r" (zero));
+
+	if (tlb_flag(TLB_BARRIER))
+		isb();
+}
+
+#ifdef CONFIG_ARM_ERRATA_798181
+static inline void dummy_flush_tlb_a15_erratum(void)
+{
+	/*
+	 * Dummy TLBIMVAIS. Using the unmapped address 0 and ASID 0.
+	 */
+	asm("mcr p15, 0, %0, c8, c3, 1" : : "r" (0));
+	dsb();
+}
+#else
+static inline void dummy_flush_tlb_a15_erratum(void)
+{
+}
+#endif
+
 /*
  *	flush_pmd_entry
  *
@@ -497,6 +515,7 @@
 #define flush_tlb_kernel_page	local_flush_tlb_kernel_page
 #define flush_tlb_range		local_flush_tlb_range
 #define flush_tlb_kernel_range	local_flush_tlb_kernel_range
+#define flush_bp_all		local_flush_bp_all
 #else
 extern void flush_tlb_all(void);
 extern void flush_tlb_mm(struct mm_struct *mm);
@@ -504,6 +523,7 @@
 extern void flush_tlb_kernel_page(unsigned long kaddr);
 extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
 extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
+extern void flush_bp_all(void);
 #endif
 
 /*
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c
index 1429d89..ebbb537 100644
--- a/arch/arm/kernel/asm-offsets.c
+++ b/arch/arm/kernel/asm-offsets.c
@@ -105,7 +105,7 @@
   BLANK();
 #endif
 #ifdef CONFIG_CPU_HAS_ASID
-  DEFINE(MM_CONTEXT_ID,		offsetof(struct mm_struct, context.id));
+  DEFINE(MM_CONTEXT_ID,		offsetof(struct mm_struct, context.id.counter));
   BLANK();
 #endif
   DEFINE(VMA_VM_MM,		offsetof(struct vm_area_struct, vm_mm));
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index 54ee265..e463331 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -444,12 +444,7 @@
 
 #ifdef CONFIG_SECCOMP
 	tst	r10, #_TIF_SECCOMP
-	beq	1f
-	mov	r0, scno
-	bl	__secure_computing	
-	add	r0, sp, #S_R0 + S_OFF		@ pointer to regs
-	ldmia	r0, {r0 - r3}			@ have to reload r0 - r3
-1:
+	bne	__sys_trace
 #endif
 
 	tst	r10, #_TIF_SYSCALL_WORK		@ are we tracing syscalls?
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index 9650c14..77da665 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -909,20 +909,22 @@
 asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno)
 {
 	unsigned long ip;
+	current_thread_info()->syscall = scno;
 
 	if (why)
 		audit_syscall_exit(regs);
-	else
+	else {
+		if (secure_computing(scno) == -1)
+			return -1;
 		audit_syscall_entry(AUDIT_ARCH_ARM, scno, regs->ARM_r0,
 				    regs->ARM_r1, regs->ARM_r2, regs->ARM_r3);
+	}
 
 	if (!test_thread_flag(TIF_SYSCALL_TRACE))
 		return scno;
 	if (!(current->ptrace & PT_PTRACED))
 		return scno;
 
-	current_thread_info()->syscall = scno;
-
 	/*
 	 * IP is used to denote syscall entry/exit:
 	 * IP = 0 -> entry, =1 -> exit
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index f90c962..ce17a01 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -257,6 +257,7 @@
 	 * switch away from it before attempting any exclusive accesses.
 	 */
 	cpu_switch_mm(mm->pgd, mm);
+	local_flush_bp_all();
 	enter_lazy_tlb(mm, current);
 	local_flush_tlb_all();
 
diff --git a/arch/arm/kernel/smp_tlb.c b/arch/arm/kernel/smp_tlb.c
index 02c5d2c..e82e1d2 100644
--- a/arch/arm/kernel/smp_tlb.c
+++ b/arch/arm/kernel/smp_tlb.c
@@ -12,6 +12,7 @@
 
 #include <asm/smp_plat.h>
 #include <asm/tlbflush.h>
+#include <asm/mmu_context.h>
 
 /**********************************************************************/
 
@@ -64,12 +65,77 @@
 	local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end);
 }
 
+static inline void ipi_flush_bp_all(void *ignored)
+{
+	local_flush_bp_all();
+}
+
+#ifdef CONFIG_ARM_ERRATA_798181
+static int erratum_a15_798181(void)
+{
+	unsigned int midr = read_cpuid_id();
+
+	/* Cortex-A15 r0p0..r3p2 affected */
+	if ((midr & 0xff0ffff0) != 0x410fc0f0 || midr > 0x413fc0f2)
+		return 0;
+	return 1;
+}
+#else
+static int erratum_a15_798181(void)
+{
+	return 0;
+}
+#endif
+
+static void ipi_flush_tlb_a15_erratum(void *arg)
+{
+	dmb();
+}
+
+static void broadcast_tlb_a15_erratum(void)
+{
+	if (!erratum_a15_798181())
+		return;
+
+	dummy_flush_tlb_a15_erratum();
+	smp_call_function_many(cpu_online_mask, ipi_flush_tlb_a15_erratum,
+			       NULL, 1);
+}
+
+static void broadcast_tlb_mm_a15_erratum(struct mm_struct *mm)
+{
+	int cpu;
+	cpumask_t mask = { CPU_BITS_NONE };
+
+	if (!erratum_a15_798181())
+		return;
+
+	dummy_flush_tlb_a15_erratum();
+	for_each_online_cpu(cpu) {
+		if (cpu == smp_processor_id())
+			continue;
+		/*
+		 * We only need to send an IPI if the other CPUs are running
+		 * the same ASID as the one being invalidated. There is no
+		 * need for locking around the active_asids check since the
+		 * switch_mm() function has at least one dmb() (as required by
+		 * this workaround) in case a context switch happens on
+		 * another CPU after the condition below.
+		 */
+		if (atomic64_read(&mm->context.id) ==
+		    atomic64_read(&per_cpu(active_asids, cpu)))
+			cpumask_set_cpu(cpu, &mask);
+	}
+	smp_call_function_many(&mask, ipi_flush_tlb_a15_erratum, NULL, 1);
+}
+
 void flush_tlb_all(void)
 {
 	if (tlb_ops_need_broadcast())
 		on_each_cpu(ipi_flush_tlb_all, NULL, 1);
 	else
 		local_flush_tlb_all();
+	broadcast_tlb_a15_erratum();
 }
 
 void flush_tlb_mm(struct mm_struct *mm)
@@ -78,6 +144,7 @@
 		on_each_cpu_mask(mm_cpumask(mm), ipi_flush_tlb_mm, mm, 1);
 	else
 		local_flush_tlb_mm(mm);
+	broadcast_tlb_mm_a15_erratum(mm);
 }
 
 void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
@@ -90,6 +157,7 @@
 					&ta, 1);
 	} else
 		local_flush_tlb_page(vma, uaddr);
+	broadcast_tlb_mm_a15_erratum(vma->vm_mm);
 }
 
 void flush_tlb_kernel_page(unsigned long kaddr)
@@ -100,6 +168,7 @@
 		on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1);
 	} else
 		local_flush_tlb_kernel_page(kaddr);
+	broadcast_tlb_a15_erratum();
 }
 
 void flush_tlb_range(struct vm_area_struct *vma,
@@ -114,6 +183,7 @@
 					&ta, 1);
 	} else
 		local_flush_tlb_range(vma, start, end);
+	broadcast_tlb_mm_a15_erratum(vma->vm_mm);
 }
 
 void flush_tlb_kernel_range(unsigned long start, unsigned long end)
@@ -125,5 +195,13 @@
 		on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1);
 	} else
 		local_flush_tlb_kernel_range(start, end);
+	broadcast_tlb_a15_erratum();
 }
 
+void flush_bp_all(void)
+{
+	if (tlb_ops_need_broadcast())
+		on_each_cpu(ipi_flush_bp_all, NULL, 1);
+	else
+		local_flush_bp_all();
+}
diff --git a/arch/arm/kernel/suspend.c b/arch/arm/kernel/suspend.c
index 1794cc3..2246e53 100644
--- a/arch/arm/kernel/suspend.c
+++ b/arch/arm/kernel/suspend.c
@@ -53,6 +53,7 @@
 	ret = __cpu_suspend(arg, fn);
 	if (ret == 0) {
 		cpu_switch_mm(mm->pgd, mm);
+		local_flush_bp_all();
 		local_flush_tlb_all();
 	}
 
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index a53a5a3..fa570a8 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -508,6 +508,10 @@
 	struct thread_info *thread = current_thread_info();
 	siginfo_t info;
 
+	/* Emulate/fallthrough. */
+	if (no == -1)
+		return regs->ARM_r0;
+
 	if ((no >> 16) != (__ARM_NR_BASE>> 16))
 		return bad_syscall(no, regs);
 
diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index 3423296..ded07c6 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -534,6 +534,61 @@
 	select EXYNOS5_DEV_BTS
 	help
 	  Machine support for Samsung SMDK5250
+
+config MACH_MANTA
+	bool "Manta"
+	select SOC_BUS
+	select SOC_EXYNOS5250
+	select S3C_DEV_HSMMC3
+	select S3C_DEV_I2C1
+	select S3C_DEV_I2C2
+	select S3C_DEV_I2C3
+	select S3C_DEV_I2C4
+	select S3C_DEV_I2C5
+	select S3C_DEV_I2C7
+	select S3C_DEV_RTC
+	select S3C_DEV_USB_HSOTG
+	select S5P_DEV_MFC
+	select S5P_DEV_DP
+	select S5P_DEV_FIMD1
+	select S5P_DEV_FIMG2D
+	select S5P_DEV_TV
+	select S5P_DEV_I2C_HDMIPHY
+	select S5P_DEV_USB_EHCI
+	select S3C_DEV_WDT
+	select S5P_GPIO_INT
+	select EXYNOS_DEV_DMA
+	select EXYNOS_DEV_SYSMMU
+	select EXYNOS_DEV_DWMCI
+	select EXYNOS_DEV_DMA
+	select EXYNOS_PERSISTENT_CLOCK
+	select EXYNOS_SETUP_ADC
+	select EXYNOS_SETUP_DP
+	select EXYNOS_SETUP_FIMD1
+	select EXYNOS_DEV_ROTATOR
+	select EXYNOS_DEV_TMU
+	select EXYNOS4_DEV_FIMC_IS
+	select EXYNOS4_DEV_USB_OHCI
+	select EXYNOS4_SETUP_I2C1
+	select EXYNOS4_SETUP_I2C2
+	select EXYNOS4_SETUP_I2C3
+	select EXYNOS4_SETUP_I2C4
+	select EXYNOS4_SETUP_I2C5
+	select EXYNOS4_SETUP_I2C7
+	select EXYNOS4_SETUP_MFC
+	select EXYNOS4_SETUP_USB_PHY
+	select EXYNOS4_SETUP_FIMC_IS
+	select EXYNOS5_DEV_BTS
+	select SAMSUNG_DEV_ADC
+	select SAMSUNG_DEV_BACKLIGHT
+	select SAMSUNG_DEV_PWM
+	select S3C64XX_DEV_SPI1
+	select S3C64XX_DEV_SPI2
+	select EXYNOS_SETUP_SPI
+	select USB_OTG_UTILS
+	help
+	  Machine support for Manta
+
 endif
 
 comment "Flattened Device Tree based board for EXYNOS SoCs"
diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile
old mode 100644
new mode 100755
index 756c49d..9c7542e6
--- a/arch/arm/mach-exynos/Makefile
+++ b/arch/arm/mach-exynos/Makefile
@@ -61,6 +61,25 @@
 
 obj-$(CONFIG_MACH_SMDK5250)		+= mach-smdk5250.o
 
+# Manta board files
+obj-$(CONFIG_MACH_MANTA)		+= board-manta.o
+obj-$(CONFIG_MACH_MANTA)		+= board-manta-audio.o
+obj-$(CONFIG_MACH_MANTA)		+= board-manta-battery.o
+obj-$(CONFIG_MACH_MANTA)		+= board-manta-pogo.o
+obj-$(CONFIG_MACH_MANTA)		+= board-manta-display.o
+obj-$(CONFIG_MACH_MANTA)		+= board-manta-input.o
+obj-$(CONFIG_MACH_MANTA)		+= board-manta-power.o
+obj-$(CONFIG_MACH_MANTA)		+= board-manta-wifi.o
+obj-$(CONFIG_MACH_MANTA)		+= board-manta-media.o
+obj-$(CONFIG_MACH_MANTA)		+= board-manta-camera.o
+obj-$(CONFIG_MACH_MANTA)		+= board-manta-sensors.o
+obj-$(CONFIG_MACH_MANTA)		+= board-manta-gps.o
+obj-$(CONFIG_MACH_MANTA)		+= board-manta-jack.o
+obj-$(CONFIG_MACH_MANTA)		+= board-manta-vibrator.o
+obj-$(CONFIG_MACH_MANTA)		+= board-manta-nfc.o
+obj-$(CONFIG_MACH_MANTA)		+= board-manta-bluetooth.o
+obj-$(CONFIG_MACH_MANTA)		+= board-manta-connector.o
+
 # device support
 
 obj-y					+= dev-uart.o
diff --git a/arch/arm/mach-exynos/board-manta-audio.c b/arch/arm/mach-exynos/board-manta-audio.c
new file mode 100644
index 0000000..a39b21c
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta-audio.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/wm8994/gpio.h>
+#include <linux/mfd/wm8994/pdata.h>
+#include <linux/platform_data/es305.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
+
+#include <mach/gpio.h>
+
+#include <plat/devs.h>
+#include <plat/gpio-cfg.h>
+
+#include "board-manta.h"
+
+#define GPIO_ES305_WAKEUP	EXYNOS5_GPG0(3)
+#define GPIO_ES305_RESET	EXYNOS5_GPG0(4)
+#define GPIO_ES305_CLK_EN	EXYNOS5_GPG0(6)
+#define GPIO_CODEC_LDO_EN	EXYNOS5_GPH1(1)
+
+static struct clk *clkout;
+
+static void manta_es305_clk_enable(bool enable)
+{
+	int hw_rev = exynos5_manta_get_revision();
+
+	if (enable) {
+		if (hw_rev >= MANTA_REV_PRE_ALPHA)
+			gpio_set_value(GPIO_ES305_CLK_EN, 1);
+		else
+			clk_enable(clkout);
+	} else {
+		if (hw_rev >= MANTA_REV_PRE_ALPHA)
+			gpio_set_value(GPIO_ES305_CLK_EN, 0);
+		else
+			clk_disable(clkout);
+	}
+}
+
+static struct es305_platform_data es305_pdata = {
+	.gpio_wakeup = GPIO_ES305_WAKEUP,
+	.gpio_reset = GPIO_ES305_RESET,
+	.clk_enable = manta_es305_clk_enable,
+	.passthrough_src = 1, /* port A */
+	.passthrough_dst = 4, /* port D */
+};
+
+static struct i2c_board_info i2c_devs4[] __initdata = {
+	{
+		I2C_BOARD_INFO("audience_es305", 0x3e),
+		.platform_data = &es305_pdata,
+	},
+};
+
+static struct regulator_consumer_supply vbatt_supplies[] = {
+	REGULATOR_SUPPLY("LDO1VDD", "7-001a"),
+	REGULATOR_SUPPLY("SPKVDD1", "7-001a"),
+	REGULATOR_SUPPLY("SPKVDD2", "7-001a"),
+};
+
+static struct regulator_init_data vbatt_initdata = {
+	.constraints = {
+		.always_on = 1,
+	},
+	.num_consumer_supplies = ARRAY_SIZE(vbatt_supplies),
+	.consumer_supplies = vbatt_supplies,
+};
+
+static struct fixed_voltage_config vbatt_config = {
+	.init_data = &vbatt_initdata,
+	.microvolts = 3700000,
+	.supply_name = "VBATT",
+	.gpio = -EINVAL,
+};
+
+static struct platform_device vbatt_device = {
+	.name = "reg-fixed-voltage",
+	.id = -1,
+	.dev = {
+		.platform_data = &vbatt_config,
+	},
+};
+
+static struct regulator_consumer_supply wm1811_ldo1_supplies[] = {
+	REGULATOR_SUPPLY("AVDD1", "7-001a"),
+};
+
+static struct regulator_init_data wm1811_ldo1_initdata = {
+	.constraints = {
+		.name = "WM1811 LDO1",
+		.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+	},
+	.num_consumer_supplies = ARRAY_SIZE(wm1811_ldo1_supplies),
+	.consumer_supplies = wm1811_ldo1_supplies,
+};
+
+static struct regulator_consumer_supply wm1811_ldo2_supplies[] = {
+	REGULATOR_SUPPLY("DCVDD", "7-001a"),
+};
+
+static struct regulator_init_data wm1811_ldo2_initdata = {
+	.constraints = {
+		.name = "WM1811 LDO2",
+		.always_on = true,  /* Actually status changed by LDO1 */
+	},
+	.num_consumer_supplies = ARRAY_SIZE(wm1811_ldo2_supplies),
+	.consumer_supplies = wm1811_ldo2_supplies,
+};
+
+static struct wm8994_drc_cfg wm1811_drc_cfgs[] = {
+	{
+		.name = "Default",
+		.regs = {0x0098, 0x0845, 0x0, 0x0, 0x0 }
+	},
+	{
+		.name = "Speakers Media",
+		.regs = {0x0098, 0x0245, 0x0028, 0x00c6, 0x0 }
+	}
+};
+
+static struct wm8958_micd_rate manta_micd_rates[] = {
+	{ 32768,	true,	0,	1 },
+	{ 32768,	false,	0,	1 },
+	{ 44100 * 256,	true,	8,	8 },
+	{ 44100 * 256,	false,	8,	8 },
+};
+
+static struct wm8994_pdata wm1811_pdata = {
+	.gpio_defaults = {
+		[0] = WM8994_GP_FN_IRQ, /* GPIO1 IRQ output, CMOS mode */
+		[7] = WM8994_GPN_DIR |
+				WM8994_GP_FN_PIN_SPECIFIC, /* DACDAT3 */
+		[8] = WM8994_CONFIGURE_GPIO |
+				WM8994_GP_FN_PIN_SPECIFIC, /* ADCDAT3 */
+		[9] = WM8994_CONFIGURE_GPIO |
+				WM8994_GP_FN_PIN_SPECIFIC, /* LRCLK3 */
+		[10] = WM8994_CONFIGURE_GPIO |
+				WM8994_GP_FN_PIN_SPECIFIC, /* BCLK3 */
+	},
+
+	/* The enable is shared but assign it to LDO1 for software */
+	.ldo = {
+		{
+			.enable = GPIO_CODEC_LDO_EN,
+			.init_data = &wm1811_ldo1_initdata,
+		},
+		{
+			.init_data = &wm1811_ldo2_initdata,
+		},
+	},
+
+	.num_drc_cfgs = ARRAY_SIZE(wm1811_drc_cfgs),
+	.drc_cfgs = wm1811_drc_cfgs,
+
+	.num_micd_rates = ARRAY_SIZE(manta_micd_rates),
+	.micd_rates = manta_micd_rates,
+
+	/* Regulated mode at highest output voltage */
+	.micbias = {0x2f, 0x2f},
+	.micd_lvl_sel = 0xff,
+
+	/* Support external capacitors*/
+	.jd_ext_cap = 1,
+
+	.ldo_ena_always_driven = true,
+	.irq_base = MANTA_IRQ_BOARD_AUDIO_START,
+
+	/*
+	 * Restrict the i2s clock for a maximum of 2 channels to keep
+	 * the bus within spec since i2s0 is shared with the HDMI block
+	 */
+	.max_channels_clocked = {2, 2, 2},
+};
+
+static struct i2c_board_info i2c_devs7[] __initdata = {
+	{
+		I2C_BOARD_INFO("wm1811", (0x34 >> 1)), /* Audio codec */
+		.platform_data = &wm1811_pdata,
+		.irq = IRQ_EINT(29),
+	},
+};
+
+static struct platform_device manta_i2s_device = {
+	.name	= "manta-i2s",
+	.id	= -1,
+};
+
+static struct platform_device manta_spdif_device = {
+	.name	= "manta-spdif",
+	.id	= -1,
+};
+
+static struct platform_device manta_spdif_dit_device = {
+	.name	= "spdif-dit",
+	.id	= -1,
+};
+
+static struct platform_device *manta_audio_devices[] __initdata = {
+	&vbatt_device,
+	&samsung_asoc_dma,
+	&samsung_asoc_idma,
+	&exynos5_device_srp,
+	&exynos5_device_i2s0,
+	&exynos5_device_pcm0,
+	&exynos5_device_spdif,
+	&manta_i2s_device,
+	&manta_spdif_device,
+	&manta_spdif_dit_device,
+};
+
+static void manta_audio_setup_clocks(void)
+{
+	struct clk *fout_epll, *mout_epll;
+	struct clk *sclk_audio, *sclk_spdif;
+	struct clk *xxti;
+	int ret;
+
+	fout_epll = clk_get(NULL, "fout_epll");
+	if (IS_ERR(fout_epll)) {
+		pr_err("%s:cannot get fout_epll clock\n", __func__);
+		return;
+	}
+
+	mout_epll = clk_get(NULL, "mout_epll");
+	if (IS_ERR(mout_epll)) {
+		pr_err("%s: cannot get mout_epll clock\n", __func__);
+		goto out1;
+	}
+
+	sclk_audio = clk_get(NULL, "sclk_audio");
+	if (IS_ERR(sclk_audio)) {
+		pr_err("%s: cannot get sclk_audio clock\n", __func__);
+		goto out2;
+	}
+
+	sclk_spdif = clk_get(NULL, "sclk_spdif");
+	if (IS_ERR(sclk_spdif)) {
+		pr_err("%s: cannot get sclk_spdif clock\n", __func__);
+		goto out3;
+	}
+
+	xxti = clk_get(NULL, "xxti");
+	if (IS_ERR(xxti)) {
+		pr_err("%s: cannot get xxti clock\n", __func__);
+		goto out4;
+	}
+
+	clkout = clk_get(NULL, "clkout");
+	if (IS_ERR(clkout)) {
+		pr_err("%s: cannot get clkout\n", __func__);
+		goto out5;
+	}
+
+	clk_set_parent(mout_epll, fout_epll);
+	clk_set_parent(sclk_audio, mout_epll);
+	clk_set_parent(sclk_spdif, sclk_audio);
+	clk_set_parent(clkout, xxti);
+	clk_add_alias("system_clk", "manta-i2s", "clkout", NULL);
+
+	ret = gpio_request(GPIO_ES305_CLK_EN, "ES305 clk_en");
+	if (ret < 0) {
+		pr_err("%s: error requesting ES305 clk_en gpio\n", __func__);
+		goto out6;
+	}
+	gpio_direction_output(GPIO_ES305_CLK_EN, 0);
+
+	clk_put(fout_epll);
+	clk_put(mout_epll);
+	clk_put(sclk_audio);
+	clk_put(sclk_spdif);
+	clk_put(xxti);
+
+	return;
+out6:
+	clk_put(clkout);
+out5:
+	clk_put(xxti);
+out4:
+	clk_put(sclk_spdif);
+out3:
+	clk_put(sclk_audio);
+out2:
+	clk_put(mout_epll);
+out1:
+	clk_put(fout_epll);
+}
+
+void __init exynos5_manta_audio_init(void)
+{
+	manta_audio_setup_clocks();
+
+	s5p_gpio_set_pd_cfg(GPIO_ES305_WAKEUP, S5P_GPIO_PD_PREV_STATE);
+	s5p_gpio_set_pd_cfg(GPIO_ES305_RESET, S5P_GPIO_PD_PREV_STATE);
+	s5p_gpio_set_pd_cfg(GPIO_ES305_CLK_EN, S5P_GPIO_PD_PREV_STATE);
+	s5p_gpio_set_pd_cfg(GPIO_CODEC_LDO_EN, S5P_GPIO_PD_PREV_STATE);
+
+	i2c_register_board_info(4, i2c_devs4, ARRAY_SIZE(i2c_devs4));
+	i2c_register_board_info(7, i2c_devs7, ARRAY_SIZE(i2c_devs7));
+
+	platform_add_devices(manta_audio_devices,
+				ARRAY_SIZE(manta_audio_devices));
+}
diff --git a/arch/arm/mach-exynos/board-manta-battery.c b/arch/arm/mach-exynos/board-manta-battery.c
new file mode 100644
index 0000000..805cdb8e
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta-battery.c
@@ -0,0 +1,1089 @@
+/* linux/arch/arm/mach-exynos/board-manta-battery.c
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.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/err.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/debugfs.h>
+#include <linux/suspend.h>
+#include <linux/pm_wakeup.h>
+#include <linux/wakelock.h>
+#include <linux/notifier.h>
+#include <linux/usb/otg.h>
+
+#include <plat/adc.h>
+#include <plat/gpio-cfg.h>
+
+#include <linux/power/smb347-charger.h>
+#include <linux/platform_data/android_battery.h>
+#include <linux/platform_data/ds2482.h>
+
+#include "board-manta.h"
+
+#include <linux/slab.h>
+
+#define TA_ADC_LOW		700
+#define TA_ADC_HIGH		1750
+
+#define ADC_NUM_SAMPLES		5
+#define ADC_LIMIT_ERR_COUNT	5
+
+#define	GPIO_USB_SEL1		EXYNOS5_GPH0(1)
+#define	GPIO_TA_EN		EXYNOS5_GPG1(5)
+#define	GPIO_TA_INT		EXYNOS5_GPX0(0)
+#define	GPIO_TA_NCHG		EXYNOS5_GPX0(4)
+#define GPIO_OTG_VBUS_SENSE	EXYNOS5_GPX1(0)
+#define GPIO_VBUS_POGO_5V	EXYNOS5_GPX1(2)
+#define GPIO_OTG_VBUS_SENSE_FAC	EXYNOS5_GPB0(1)
+#define GPIO_1WIRE_SLEEP	EXYNOS5_GPG0(0)
+
+enum charge_connector {
+	CHARGE_CONNECTOR_NONE,
+	CHARGE_CONNECTOR_POGO,
+	CHARGE_CONNECTOR_USB,
+	CHARGE_CONNECTOR_MAX,
+};
+
+static int manta_bat_battery_status;
+static enum manta_charge_source manta_bat_charge_source[CHARGE_CONNECTOR_MAX];
+static enum charge_connector manta_bat_charge_conn;
+static union power_supply_propval manta_bat_apsd_results;
+static int manta_bat_ta_adc;
+static bool manta_bat_dock;
+static bool manta_bat_usb_online;
+static bool manta_bat_pogo_online;
+static bool manta_bat_otg_enabled;
+static bool manta_bat_chg_enabled;
+static bool manta_bat_chg_enable_synced;
+static struct power_supply *manta_bat_smb347_mains;
+static struct power_supply *manta_bat_smb347_usb;
+static struct power_supply *manta_bat_smb347_battery;
+static struct power_supply *manta_bat_ds2784_battery;
+
+static struct android_bat_callbacks *bat_callbacks;
+
+static struct s3c_adc_client *ta_adc_client;
+
+static struct wake_lock manta_bat_chgdetect_wakelock;
+
+static struct delayed_work redetect_work;
+static struct wake_lock manta_bat_redetect_wl;
+
+static DEFINE_MUTEX(manta_bat_charger_detect_lock);
+static DEFINE_MUTEX(manta_bat_adc_lock);
+
+static bool manta_bat_suspended;
+
+static inline int manta_source_to_android(enum manta_charge_source src)
+{
+	switch (src) {
+	case MANTA_CHARGE_SOURCE_NONE:
+		return CHARGE_SOURCE_NONE;
+	case MANTA_CHARGE_SOURCE_USB:
+	case MANTA_CHARGE_SOURCE_UNKNOWN:
+		return CHARGE_SOURCE_USB;
+	case MANTA_CHARGE_SOURCE_AC_SAMSUNG:
+	case MANTA_CHARGE_SOURCE_AC_OTHER:
+		return CHARGE_SOURCE_AC;
+	default:
+		break;
+	}
+
+	return CHARGE_SOURCE_NONE;
+}
+
+static inline int manta_bat_get_ds2784(void)
+{
+	if (!manta_bat_ds2784_battery)
+		manta_bat_ds2784_battery =
+			power_supply_get_by_name("ds2784-fuelgauge");
+
+	if (!manta_bat_ds2784_battery) {
+		pr_err_once("%s: failed to get ds2784-fuelgauge power supply\n",
+			    __func__);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static inline int manta_bat_get_smb347_usb(void)
+{
+	if (!manta_bat_smb347_usb)
+		manta_bat_smb347_usb =
+			power_supply_get_by_name("smb347-usb");
+
+	if (!manta_bat_smb347_usb) {
+		pr_err("%s: failed to get smb347-usb power supply\n",
+		       __func__);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void charger_gpio_init(void)
+{
+	int ret;
+
+	s3c_gpio_cfgpin(GPIO_TA_INT, S3C_GPIO_INPUT);
+	s3c_gpio_setpull(GPIO_TA_INT, S3C_GPIO_PULL_NONE);
+
+	s3c_gpio_cfgpin(GPIO_OTG_VBUS_SENSE, S3C_GPIO_INPUT);
+	s3c_gpio_setpull(GPIO_OTG_VBUS_SENSE, S3C_GPIO_PULL_NONE);
+
+	s3c_gpio_cfgpin(GPIO_VBUS_POGO_5V, S3C_GPIO_INPUT);
+	s3c_gpio_setpull(GPIO_VBUS_POGO_5V, S3C_GPIO_PULL_NONE);
+
+	s3c_gpio_cfgpin(GPIO_OTG_VBUS_SENSE_FAC, S3C_GPIO_INPUT);
+	s3c_gpio_setpull(GPIO_OTG_VBUS_SENSE_FAC, S3C_GPIO_PULL_NONE);
+	s5p_gpio_set_pd_cfg(GPIO_OTG_VBUS_SENSE_FAC, S5P_GPIO_PD_PREV_STATE);
+	s5p_gpio_set_pd_pull(GPIO_OTG_VBUS_SENSE_FAC,
+			     S5P_GPIO_PD_UPDOWN_DISABLE);
+
+	s3c_gpio_cfgpin(GPIO_TA_NCHG, S3C_GPIO_INPUT);
+	s3c_gpio_setpull(GPIO_TA_NCHG, S3C_GPIO_PULL_NONE);
+
+	s3c_gpio_cfgpin(GPIO_TA_EN, S3C_GPIO_OUTPUT);
+	s3c_gpio_setpull(GPIO_TA_EN, S3C_GPIO_PULL_NONE);
+	s5p_gpio_set_pd_cfg(GPIO_TA_EN, S5P_GPIO_PD_PREV_STATE);
+	s5p_gpio_set_pd_pull(GPIO_TA_EN, S5P_GPIO_PD_UPDOWN_DISABLE);
+
+	s3c_gpio_cfgpin(GPIO_USB_SEL1, S3C_GPIO_OUTPUT);
+	s3c_gpio_setpull(GPIO_USB_SEL1, S3C_GPIO_PULL_NONE);
+	s5p_gpio_set_pd_pull(GPIO_USB_SEL1, S5P_GPIO_PD_UPDOWN_DISABLE);
+	ret = gpio_request_one(GPIO_USB_SEL1, GPIOF_OUT_INIT_HIGH, "usb_sel1");
+	if (ret)
+		pr_err("%s: cannot request gpio%d\n", __func__, GPIO_USB_SEL1);
+}
+
+static int read_ta_adc(enum charge_connector conn, int ta_check_sel)
+{
+	int adc_max = -1;
+	int adc_min = 1 << 11;
+	int adc_total = 0;
+	int i, j;
+	int ret;
+
+	mutex_lock(&manta_bat_adc_lock);
+
+	/* switch to check adc */
+	if (conn == CHARGE_CONNECTOR_USB)
+		gpio_set_value(GPIO_USB_SEL1, 0);
+	else {
+		ret = manta_pogo_charge_detect_start(ta_check_sel);
+		if (ret < 0) {
+			pr_err("%s: Failed to start pogo charger detection\n",
+				__func__);
+			goto fail_gpio;
+		}
+	}
+
+	msleep(100);
+
+	for (i = 0; i < ADC_NUM_SAMPLES; i++) {
+		ret = s3c_adc_read(ta_adc_client, 0);
+
+		if (ret == -ETIMEDOUT) {
+			for (j = 0; j < ADC_LIMIT_ERR_COUNT; j++) {
+				msleep(20);
+				ret = s3c_adc_read(ta_adc_client, 0);
+				if (ret > 0)
+					break;
+			}
+			if (j >= ADC_LIMIT_ERR_COUNT) {
+				pr_err("%s: Retry count exceeded\n", __func__);
+				goto out;
+			}
+		} else if (ret < 0) {
+			pr_err("%s: Failed read adc value : %d\n",
+				__func__, ret);
+			goto out;
+		}
+
+		if (ret > adc_max)
+			adc_max = ret;
+		if (ret < adc_min)
+			adc_min = ret;
+
+		adc_total += ret;
+	}
+
+	ret = (adc_total - adc_max - adc_min) / (ADC_NUM_SAMPLES - 2);
+
+out:
+	msleep(50);
+
+	/* switch back to normal */
+	if (conn == CHARGE_CONNECTOR_USB)
+		gpio_set_value(GPIO_USB_SEL1, 1);
+	else
+		manta_pogo_charge_detect_end();
+
+fail_gpio:
+	mutex_unlock(&manta_bat_adc_lock);
+	return ret;
+}
+
+int manta_bat_otg_enable(bool enable)
+{
+	int ret;
+	union power_supply_propval value;
+
+	if (manta_bat_get_smb347_usb())
+		return -ENODEV;
+
+	mutex_lock(&manta_bat_charger_detect_lock);
+
+	if (manta_bat_otg_enabled == enable) {
+		mutex_unlock(&manta_bat_charger_detect_lock);
+		return 0;
+	}
+
+	value.intval = enable ? 1 : 0;
+	ret = manta_bat_smb347_usb->set_property(manta_bat_smb347_usb,
+						 POWER_SUPPLY_PROP_USB_OTG,
+						 &value);
+	if (ret)
+		pr_err("%s: failed to set smb347-usb OTG mode\n",
+		       __func__);
+	else
+		manta_bat_otg_enabled = enable;
+
+	mutex_unlock(&manta_bat_charger_detect_lock);
+	return ret;
+}
+
+static enum manta_charge_source check_samsung_charger(
+	enum charge_connector conn, bool usbin_redetect)
+{
+	int ret;
+	bool samsung_ac_detect;
+	enum manta_charge_source charge_source;
+	union power_supply_propval prop_zero = {0,};
+	union power_supply_propval prop_one = {1,};
+
+	if (conn == CHARGE_CONNECTOR_POGO) {
+		manta_bat_ta_adc = read_ta_adc(conn, 0);
+		pr_debug("%s: ta_adc conn=%d ta_check=0 val=%d\n", __func__,
+			 conn, manta_bat_ta_adc);
+		samsung_ac_detect = manta_bat_ta_adc > TA_ADC_LOW &&
+			manta_bat_ta_adc < TA_ADC_HIGH;
+		charge_source = samsung_ac_detect ?
+			MANTA_CHARGE_SOURCE_AC_OTHER :
+			MANTA_CHARGE_SOURCE_USB;
+	} else {
+		if (manta_bat_get_smb347_usb())
+			return MANTA_CHARGE_SOURCE_UNKNOWN;
+		ret = manta_bat_smb347_usb->set_property(
+			manta_bat_smb347_usb,
+			POWER_SUPPLY_PROP_CHARGER_DETECTION,
+			&prop_one);
+		if (ret)
+			pr_err("%s: failed to enable smb347-usb charger detect\n",
+			       __func__);
+		ret = manta_bat_smb347_usb->get_property(
+			manta_bat_smb347_usb,
+			POWER_SUPPLY_PROP_REMOTE_TYPE, &manta_bat_apsd_results);
+		pr_debug("%s: type=%d ret=%d\n", __func__,
+			 manta_bat_apsd_results.intval, ret);
+		samsung_ac_detect =
+			manta_bat_apsd_results.intval ==
+			POWER_SUPPLY_TYPE_USB_DCP;
+
+		switch (manta_bat_apsd_results.intval) {
+		case POWER_SUPPLY_TYPE_USB:
+			charge_source = MANTA_CHARGE_SOURCE_USB;
+			break;
+		case POWER_SUPPLY_TYPE_UNKNOWN:
+			charge_source = MANTA_CHARGE_SOURCE_UNKNOWN;
+			break;
+		default:
+			charge_source = MANTA_CHARGE_SOURCE_AC_OTHER;
+			break;
+		}
+
+		/*
+		 * Leave APSD on if unknown power source (possibly timeout)
+		 * and not re-detecting USBIN.  If redetecting USBIN and
+		 * power source is still unknown then disable APSD and assume
+		 * a slow charger.
+		 */
+
+		if (usbin_redetect ||
+		    manta_bat_apsd_results.intval != POWER_SUPPLY_TYPE_UNKNOWN)
+			ret = manta_bat_smb347_usb->set_property(
+				manta_bat_smb347_usb,
+				POWER_SUPPLY_PROP_CHARGER_DETECTION,
+				&prop_zero);
+	}
+
+	if (samsung_ac_detect) {
+		bool samsung_ta_detected;
+
+		manta_bat_ta_adc = read_ta_adc(conn, 1);
+		pr_debug("%s: ta_adc conn=%d ta_check=1 val=%d\n",
+			 __func__, conn, manta_bat_ta_adc);
+		samsung_ta_detected =
+			manta_bat_ta_adc > TA_ADC_LOW &&
+			manta_bat_ta_adc < TA_ADC_HIGH;
+
+		if (samsung_ta_detected)
+			charge_source = MANTA_CHARGE_SOURCE_AC_SAMSUNG;
+	}
+
+	return charge_source;
+}
+
+static enum manta_charge_source
+detect_charge_source(enum charge_connector conn, bool online,
+		     bool force_dock_redetect, bool usbin_redetect)
+{
+	enum manta_charge_source charge_source;
+	int ret;
+
+	manta_bat_dock = false;
+
+	if (!online) {
+		if (conn == CHARGE_CONNECTOR_POGO)
+			manta_pogo_set_vbus(online, NULL);
+		return MANTA_CHARGE_SOURCE_NONE;
+	}
+
+	charge_source = force_dock_redetect ?
+		MANTA_CHARGE_SOURCE_USB :
+		check_samsung_charger(conn, usbin_redetect);
+
+	if (conn == CHARGE_CONNECTOR_POGO &&
+	    charge_source == MANTA_CHARGE_SOURCE_USB) {
+		ret = manta_pogo_set_vbus(online, &charge_source);
+		manta_bat_dock = ret >= 0;
+	}
+
+	return charge_source;
+}
+
+static int update_charging_status(bool usb_connected, bool pogo_connected,
+				  bool force_dock_redetect,
+				  bool usbin_redetect)
+{
+	enum charge_connector old_charge_conn;
+	int ret = 0;
+
+	if (manta_bat_usb_online != usb_connected || usbin_redetect) {
+		bool usb_conn_src_usb;
+
+		manta_bat_usb_online = usb_connected;
+
+		if (usbin_redetect)
+			manta_otg_set_usb_state(false);
+
+		manta_bat_charge_source[CHARGE_CONNECTOR_USB] =
+			detect_charge_source(CHARGE_CONNECTOR_USB,
+					     usb_connected, false,
+					     usbin_redetect);
+		usb_conn_src_usb =
+			manta_bat_charge_source[CHARGE_CONNECTOR_USB] ==
+			MANTA_CHARGE_SOURCE_USB;
+
+		/*
+		 * If USB disconnected, cancel any pending USB charger
+		 * redetect.
+		 */
+
+		if (!usb_conn_src_usb) {
+			ret = cancel_delayed_work(&redetect_work);
+			if (ret)
+				wake_unlock(&manta_bat_redetect_wl);
+		}
+
+		manta_otg_set_usb_state(usb_conn_src_usb);
+
+		if (!usbin_redetect &&
+		    (usb_conn_src_usb ||
+		     manta_bat_charge_source[CHARGE_CONNECTOR_USB] ==
+		     MANTA_CHARGE_SOURCE_UNKNOWN)) {
+			cancel_delayed_work(&redetect_work);
+			wake_lock(&manta_bat_redetect_wl);
+			schedule_delayed_work(&redetect_work,
+					      msecs_to_jiffies(5000));
+		}
+
+		ret = 1;
+	}
+
+	if (manta_bat_pogo_online != pogo_connected || force_dock_redetect) {
+		manta_bat_pogo_online = pogo_connected;
+		manta_bat_charge_source[CHARGE_CONNECTOR_POGO] =
+			detect_charge_source(CHARGE_CONNECTOR_POGO,
+					     pogo_connected,
+					     force_dock_redetect, false);
+		ret = 1;
+	}
+
+	old_charge_conn = manta_bat_charge_conn;
+
+	if (manta_bat_charge_source[CHARGE_CONNECTOR_POGO] ==
+	    MANTA_CHARGE_SOURCE_NONE &&
+	    manta_bat_charge_source[CHARGE_CONNECTOR_USB] ==
+	    MANTA_CHARGE_SOURCE_NONE)
+		manta_bat_charge_conn = CHARGE_CONNECTOR_NONE;
+	else
+		manta_bat_charge_conn =
+			(manta_bat_charge_source[CHARGE_CONNECTOR_POGO] >=
+			 manta_bat_charge_source[CHARGE_CONNECTOR_USB]) ?
+			CHARGE_CONNECTOR_POGO : CHARGE_CONNECTOR_USB;
+
+	if (old_charge_conn != manta_bat_charge_conn)
+		ret = 1;
+
+	return ret;
+}
+
+static void manta_bat_set_charging_enable(int en)
+{
+	union power_supply_propval value;
+
+	manta_bat_chg_enabled = en;
+
+	if (!manta_bat_smb347_battery)
+		manta_bat_smb347_battery =
+			power_supply_get_by_name("smb347-battery");
+
+	if (!manta_bat_smb347_battery)
+		return;
+
+	value.intval = en ? 1 : 0;
+	manta_bat_smb347_battery->set_property(
+		manta_bat_smb347_battery, POWER_SUPPLY_PROP_CHARGE_ENABLED,
+		&value);
+	manta_bat_chg_enable_synced = true;
+}
+
+static void manta_bat_sync_charge_enable(void)
+{
+	union power_supply_propval chg_enabled = {0,};
+
+	if (!manta_bat_smb347_battery)
+		return;
+
+	manta_bat_smb347_battery->get_property(
+		manta_bat_smb347_battery,
+		POWER_SUPPLY_PROP_CHARGE_ENABLED,
+		&chg_enabled);
+
+	if (chg_enabled.intval != manta_bat_chg_enabled) {
+		if (manta_bat_chg_enable_synced) {
+			pr_info("%s: charger changed enable state to %d\n",
+				__func__, chg_enabled.intval);
+			manta_bat_chg_enabled = chg_enabled.intval;
+		}
+
+		manta_bat_set_charging_enable(manta_bat_chg_enabled);
+	}
+}
+
+static void exynos5_manta_set_priority(void)
+{
+	int ret;
+	union power_supply_propval value;
+
+	if (!manta_bat_smb347_battery)
+		manta_bat_smb347_battery =
+			power_supply_get_by_name("smb347-battery");
+
+	if (!manta_bat_smb347_battery) {
+		pr_err("%s: failed to get smb347-battery power supply\n",
+		       __func__);
+		return;
+	}
+
+	/* set SMB347 INPUT SOURCE PRIORITY = DCIN or USBIN */
+	value.intval = manta_bat_charge_conn == CHARGE_CONNECTOR_USB;
+	ret = manta_bat_smb347_battery->set_property(
+		manta_bat_smb347_battery, POWER_SUPPLY_PROP_USB_INPRIORITY,
+		&value);
+	if (ret)
+		pr_err("%s: failed to set smb347 input source priority: %d\n",
+		       __func__, ret);
+
+	/* enable SMB347 AICL for other TA */
+	value.intval =
+		manta_bat_charge_source[manta_bat_charge_conn] ==
+		MANTA_CHARGE_SOURCE_AC_OTHER;
+	ret = manta_bat_smb347_battery->set_property(
+		manta_bat_smb347_battery, POWER_SUPPLY_PROP_AUTO_CURRENT_LIMIT,
+		&value);
+	if (ret)
+		pr_err("%s: failed to set smb347 AICL: %d\n",
+		       __func__, ret);
+}
+
+static void change_charger_status(bool force_dock_redetect,
+				  bool usbin_redetect)
+{
+	int ta_int;
+	union power_supply_propval pogo_connected = {0,};
+	union power_supply_propval usb_connected = {0,};
+	union power_supply_propval smb347_status = {0,};
+	int status_change = 0;
+	int ret;
+
+	mutex_lock(&manta_bat_charger_detect_lock);
+	ta_int = gpio_get_value(GPIO_OTG_VBUS_SENSE) |
+		gpio_get_value(GPIO_VBUS_POGO_5V);
+
+	if (!manta_bat_smb347_mains || !manta_bat_smb347_usb ||
+	    !manta_bat_smb347_battery) {
+		manta_bat_smb347_mains =
+			power_supply_get_by_name("smb347-mains");
+		manta_bat_get_smb347_usb();
+		manta_bat_smb347_battery =
+			power_supply_get_by_name("smb347-battery");
+
+		if (!manta_bat_smb347_mains || !manta_bat_smb347_usb ||
+		    !manta_bat_smb347_battery)
+			pr_err("%s: failed to get power supplies\n", __func__);
+	}
+
+	if (!manta_bat_otg_enabled)
+		usb_connected.intval = gpio_get_value(GPIO_OTG_VBUS_SENSE);
+
+	pogo_connected.intval = gpio_get_value(GPIO_VBUS_POGO_5V);
+
+	if (manta_bat_smb347_usb &&
+	    usb_connected.intval != manta_bat_usb_online) {
+		ret = manta_bat_smb347_usb->set_property(
+			manta_bat_smb347_usb, POWER_SUPPLY_PROP_ONLINE,
+			&usb_connected);
+		if (ret)
+			pr_err("%s: failed to change smb347-usb online\n",
+			       __func__);
+	}
+
+	if (manta_bat_smb347_mains &&
+	    pogo_connected.intval != manta_bat_pogo_online) {
+		ret = manta_bat_smb347_mains->set_property(
+			manta_bat_smb347_mains, POWER_SUPPLY_PROP_ONLINE,
+			&pogo_connected);
+		if (ret)
+			pr_err("%s: failed to change smb347-mains online\n",
+			       __func__);
+	}
+
+	if (manta_bat_smb347_battery) {
+		manta_bat_smb347_battery->get_property(
+			manta_bat_smb347_battery, POWER_SUPPLY_PROP_STATUS,
+			&smb347_status);
+
+		if (smb347_status.intval != manta_bat_battery_status) {
+			if (smb347_status.intval == POWER_SUPPLY_STATUS_FULL &&
+			    bat_callbacks && bat_callbacks->battery_set_full)
+				bat_callbacks->battery_set_full(
+					bat_callbacks);
+
+			manta_bat_battery_status = smb347_status.intval;
+		}
+	}
+
+	if (ta_int) {
+		status_change = update_charging_status(
+			usb_connected.intval, pogo_connected.intval,
+			force_dock_redetect, usbin_redetect);
+		manta_bat_sync_charge_enable();
+	} else {
+		status_change = update_charging_status(false, false, false,
+						       false);
+	}
+
+	pr_debug("%s: ta_int(%d), charge_conn(%d), charge_source(%d)\n",
+		 __func__, ta_int, manta_bat_charge_conn,
+		 manta_bat_charge_source[manta_bat_charge_conn]);
+
+	if (status_change)
+		exynos5_manta_set_priority();
+
+	if (status_change && bat_callbacks &&
+	    bat_callbacks->charge_source_changed)
+		bat_callbacks->charge_source_changed(
+			bat_callbacks,
+			manta_source_to_android(
+				manta_bat_charge_source[manta_bat_charge_conn]));
+	mutex_unlock(&manta_bat_charger_detect_lock);
+}
+
+void manta_force_update_pogo_charger(void)
+{
+	change_charger_status(true, false);
+}
+
+static char *exynos5_manta_supplicant[] = { "manta-board" };
+
+static struct smb347_charger_platform_data smb347_chg_pdata = {
+	.use_mains = true,
+	.use_usb = true,
+	.enable_control = SMB347_CHG_ENABLE_PIN_ACTIVE_LOW,
+	.usb_mode_pin_ctrl = false,
+	.max_charge_current = 2500000,
+	.max_charge_voltage = 4300000,
+	.disable_automatic_recharge = true,
+	.pre_charge_current = 200000,
+	.termination_current = 250000,
+	.pre_to_fast_voltage = 2600000,
+	.mains_current_limit = 2000000,
+	.usb_hc_current_limit = 1800000,
+	.irq_gpio = GPIO_TA_NCHG,
+	.disable_stat_interrupts = true,
+	.en_gpio = GPIO_TA_EN,
+	.supplied_to = exynos5_manta_supplicant,
+	.num_supplicants = ARRAY_SIZE(exynos5_manta_supplicant),
+};
+
+static void manta_bat_register_callbacks(struct android_bat_callbacks *ptr)
+{
+	bat_callbacks = ptr;
+}
+
+static void manta_bat_unregister_callbacks(void)
+{
+	bat_callbacks = NULL;
+}
+
+static int manta_bat_poll_charge_source(void)
+{
+	change_charger_status(false, false);
+	return manta_source_to_android(
+		manta_bat_charge_source[manta_bat_charge_conn]);
+}
+
+static void exynos5_manta_set_mains_current(void)
+{
+	int ret;
+	union power_supply_propval value;
+
+	if (!manta_bat_smb347_mains)
+		manta_bat_smb347_mains =
+			power_supply_get_by_name("smb347-mains");
+
+	if (!manta_bat_smb347_mains) {
+		pr_err("%s: failed to get smb347-mains power supply\n",
+		       __func__);
+		return;
+	}
+
+	value.intval =
+		manta_bat_charge_source[CHARGE_CONNECTOR_POGO] ==
+		   MANTA_CHARGE_SOURCE_USB ? 500000 : 2000000;
+
+	ret = manta_bat_smb347_mains->set_property(manta_bat_smb347_mains,
+					 POWER_SUPPLY_PROP_CURRENT_MAX,
+					 &value);
+	if (ret)
+		pr_err("%s: failed to set smb347-mains current limit\n",
+		       __func__);
+}
+
+static void exynos5_manta_set_usb_hc(void)
+{
+	int ret;
+	union power_supply_propval value;
+
+	if (manta_bat_get_smb347_usb())
+		return;
+
+	value.intval =
+		manta_bat_charge_source[CHARGE_CONNECTOR_USB] ==
+		MANTA_CHARGE_SOURCE_USB ? 0 : 1;
+	ret = manta_bat_smb347_usb->set_property(manta_bat_smb347_usb,
+						 POWER_SUPPLY_PROP_USB_HC,
+						 &value);
+	if (ret)
+		pr_err("%s: failed to set smb347-usb USB/HC mode\n",
+		       __func__);
+}
+
+static void manta_bat_set_charging_current(
+	int android_charge_source)
+{
+	exynos5_manta_set_priority();
+	exynos5_manta_set_usb_hc();
+	exynos5_manta_set_mains_current();
+}
+
+static int manta_bat_get_capacity(void)
+{
+	union power_supply_propval soc;
+	int ret = -ENXIO;
+
+	if (manta_bat_get_ds2784())
+		return ret;
+	ret = manta_bat_ds2784_battery->get_property(
+		manta_bat_ds2784_battery, POWER_SUPPLY_PROP_CAPACITY,
+		&soc);
+	if (ret >= 0)
+		ret = soc.intval;
+	return ret;
+}
+
+static int manta_bat_get_temperature(int *temp_now)
+{
+	union power_supply_propval temp;
+	int ret = -ENXIO;
+
+	if (manta_bat_get_ds2784())
+		return ret;
+	ret = manta_bat_ds2784_battery->get_property(
+		manta_bat_ds2784_battery, POWER_SUPPLY_PROP_TEMP, &temp);
+	if (ret >= 0)
+		*temp_now = temp.intval;
+	return ret;
+}
+
+static int manta_bat_get_voltage_now(void)
+{
+	union power_supply_propval vcell;
+	int ret = -ENXIO;
+
+	if (manta_bat_get_ds2784())
+		return ret;
+	ret = manta_bat_ds2784_battery->get_property(
+		manta_bat_ds2784_battery, POWER_SUPPLY_PROP_VOLTAGE_NOW,
+		&vcell);
+	if (ret >= 0)
+		ret = vcell.intval;
+	return ret;
+}
+
+static int manta_bat_get_current_now(int *i_current)
+{
+	union power_supply_propval inow;
+	int ret = -ENXIO;
+
+	if (manta_bat_get_ds2784())
+		return ret;
+	ret = manta_bat_ds2784_battery->get_property(
+		manta_bat_ds2784_battery, POWER_SUPPLY_PROP_CURRENT_NOW,
+		&inow);
+	if (ret >= 0)
+		*i_current = inow.intval;
+	return ret;
+}
+
+static irqreturn_t ta_int_intr(int irq, void *arg)
+{
+	wake_lock(&manta_bat_chgdetect_wakelock);
+	msleep(600);
+	change_charger_status(false, false);
+	wake_unlock(&manta_bat_chgdetect_wakelock);
+	return IRQ_HANDLED;
+}
+
+static struct android_bat_platform_data android_battery_pdata = {
+	.register_callbacks = manta_bat_register_callbacks,
+	.unregister_callbacks = manta_bat_unregister_callbacks,
+
+	.poll_charge_source = manta_bat_poll_charge_source,
+
+	.set_charging_current = manta_bat_set_charging_current,
+	.set_charging_enable = manta_bat_set_charging_enable,
+
+	.get_capacity = manta_bat_get_capacity,
+	.get_temperature = manta_bat_get_temperature,
+	.get_voltage_now = manta_bat_get_voltage_now,
+	.get_current_now = manta_bat_get_current_now,
+
+	.temp_high_threshold = 600,	/* 60c */
+	.temp_high_recovery = 420,	/* 42c */
+	.temp_low_recovery = 0,		/* 0c */
+	.temp_low_threshold = -50,	/* -5c */
+	.full_charging_time = 12 * 60 * 60,
+	.recharging_time = 2 * 60 * 60,
+	.recharging_voltage = 4250 * 1000,
+};
+
+static struct platform_device android_device_battery = {
+	.name = "android-battery",
+	.id = -1,
+	.dev.platform_data = &android_battery_pdata,
+};
+
+static struct platform_device *manta_battery_devices[] __initdata = {
+	&android_device_battery,
+};
+
+static char *manta_charge_source_str(enum manta_charge_source charge_source)
+{
+	switch (charge_source) {
+	case MANTA_CHARGE_SOURCE_NONE:
+		return "none";
+	case MANTA_CHARGE_SOURCE_AC_SAMSUNG:
+		return "ac-samsung";
+	case MANTA_CHARGE_SOURCE_AC_OTHER:
+		return "ac-other";
+	case MANTA_CHARGE_SOURCE_USB:
+		return "usb";
+	case MANTA_CHARGE_SOURCE_UNKNOWN:
+		return "unknown";
+	default:
+		break;
+	}
+
+	return "?";
+}
+
+
+static int manta_power_debug_dump(struct seq_file *s, void *unused)
+{
+	seq_printf(s, "ta_en=%d ta_nchg=%d ta_int=%d usbin=%d, dcin=%d st=%d\n",
+		   gpio_get_value(GPIO_TA_EN),
+		   gpio_get_value(GPIO_TA_NCHG),
+		   gpio_get_value(GPIO_TA_INT),
+		   gpio_get_value(GPIO_OTG_VBUS_SENSE),
+		   gpio_get_value(GPIO_VBUS_POGO_5V),
+		   manta_bat_battery_status);
+	seq_printf(s, "%susb: type=%s (apsd=%d); %spogo: type=%s%s; ta_adc=%d\n",
+		   manta_bat_charge_conn == CHARGE_CONNECTOR_USB ? "*" : "",
+		   manta_bat_otg_enabled ? "otg" :
+		   manta_charge_source_str(
+			   manta_bat_charge_source[CHARGE_CONNECTOR_USB]),
+		   manta_bat_apsd_results.intval,
+		   manta_bat_charge_conn == CHARGE_CONNECTOR_POGO ? "*" : "",
+		   manta_charge_source_str(
+			   manta_bat_charge_source[CHARGE_CONNECTOR_POGO]),
+		   manta_bat_dock ? "(d)" : "", manta_bat_ta_adc);
+	return 0;
+}
+
+static int manta_power_debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, manta_power_debug_dump, inode->i_private);
+}
+
+static const struct file_operations manta_power_debug_fops = {
+	.open = manta_power_debug_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int manta_power_adc_debug_dump(struct seq_file *s, void *unused)
+{
+	seq_printf(s, "usb=%d,%d pogo=%d,%d\n",
+		   read_ta_adc(CHARGE_CONNECTOR_USB, 0),
+		   read_ta_adc(CHARGE_CONNECTOR_USB, 1),
+		   read_ta_adc(CHARGE_CONNECTOR_POGO, 0),
+		   read_ta_adc(CHARGE_CONNECTOR_POGO, 1));
+	return 0;
+}
+
+static int manta_power_adc_debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, manta_power_adc_debug_dump, inode->i_private);
+}
+
+static const struct file_operations manta_power_adc_debug_fops = {
+	.open = manta_power_adc_debug_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static struct ds2482_platform_data ds2483_pdata = {
+	.slpz_gpio = -1,
+};
+
+static struct i2c_board_info i2c_devs2[] __initdata = {
+	{
+		I2C_BOARD_INFO("ds2482", 0x30 >> 1),
+		.platform_data = &ds2483_pdata,
+	},
+	{
+		I2C_BOARD_INFO("smb347", 0x0c >> 1),
+		.platform_data  = &smb347_chg_pdata,
+	},
+};
+
+static void redetect_work_proc(struct work_struct *work)
+{
+	change_charger_status(false, true);
+	wake_unlock(&manta_bat_redetect_wl);
+}
+
+void __init exynos5_manta_battery_init(void)
+{
+	int hw_rev = exynos5_manta_get_revision();
+
+	charger_gpio_init();
+	INIT_DELAYED_WORK(&redetect_work, redetect_work_proc);
+	wake_lock_init(&manta_bat_chgdetect_wakelock, WAKE_LOCK_SUSPEND,
+		       "manta-chgdetect");
+	wake_lock_init(&manta_bat_redetect_wl, WAKE_LOCK_SUSPEND,
+		       "manta-chgredetect");
+
+	platform_add_devices(manta_battery_devices,
+		ARRAY_SIZE(manta_battery_devices));
+
+	if (hw_rev >= MANTA_REV_DOGFOOD02) {
+		s3c_gpio_cfgpin(GPIO_1WIRE_SLEEP, S3C_GPIO_OUTPUT);
+		s3c_gpio_setpull(GPIO_1WIRE_SLEEP, S3C_GPIO_PULL_NONE);
+		s5p_gpio_set_pd_cfg(GPIO_1WIRE_SLEEP, S5P_GPIO_PD_INPUT);
+		s5p_gpio_set_pd_pull(GPIO_1WIRE_SLEEP,
+				     S5P_GPIO_PD_UPDOWN_DISABLE);
+		ds2483_pdata.slpz_gpio = GPIO_1WIRE_SLEEP;
+	}
+
+	i2c_register_board_info(2, i2c_devs2, ARRAY_SIZE(i2c_devs2));
+
+	ta_adc_client =
+		s3c_adc_register(&android_device_battery, NULL, NULL, 0);
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("manta-power", S_IRUGO, NULL,
+					       NULL, &manta_power_debug_fops)))
+		pr_err("failed to create manta-power debugfs entry\n");
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("manta-power-adc", S_IRUGO, NULL,
+					       NULL,
+					       &manta_power_adc_debug_fops)))
+		pr_err("failed to create manta-power-adc debugfs entry\n");
+}
+
+static void exynos5_manta_power_changed(struct power_supply *psy)
+{
+	change_charger_status(false, false);
+}
+
+static int exynos5_manta_power_get_property(struct power_supply *psy,
+	    enum power_supply_property psp,
+	    union power_supply_propval *val)
+{
+	return -EINVAL;
+}
+
+static struct power_supply exynos5_manta_power_supply = {
+	.name = "manta-board",
+	.type = POWER_SUPPLY_TYPE_UNKNOWN,
+	.external_power_changed = exynos5_manta_power_changed,
+	.get_property = exynos5_manta_power_get_property,
+};
+
+static int exynos5_manta_battery_pm_event(struct notifier_block *notifier,
+					  unsigned long pm_event,
+					  void *unused)
+{
+	switch (pm_event) {
+	case PM_SUSPEND_PREPARE:
+		disable_irq(gpio_to_irq(GPIO_OTG_VBUS_SENSE));
+		disable_irq(gpio_to_irq(GPIO_VBUS_POGO_5V));
+		manta_bat_suspended = true;
+		break;
+
+	case PM_POST_SUSPEND:
+		if (manta_bat_suspended) {
+			enable_irq(gpio_to_irq(GPIO_OTG_VBUS_SENSE));
+			enable_irq(gpio_to_irq(GPIO_VBUS_POGO_5V));
+			manta_bat_suspended = false;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+static int manta_bat_usb_event(struct notifier_block *nb,
+			       unsigned long event, void *unused)
+{
+	int ret;
+
+	if (event == USB_EVENT_ENUMERATED) {
+		ret = __cancel_delayed_work(&redetect_work);
+		if (ret)
+			wake_unlock(&manta_bat_redetect_wl);
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block exynos5_manta_battery_pm_notifier_block = {
+	.notifier_call = exynos5_manta_battery_pm_event,
+};
+
+static struct notifier_block manta_bat_usb_nb = {
+	.notifier_call = manta_bat_usb_event,
+};
+
+static int __init exynos5_manta_battery_late_init(void)
+{
+	int ret;
+	struct usb_phy *usb_xceiv;
+
+	ret = power_supply_register(NULL, &exynos5_manta_power_supply);
+	if (ret)
+		pr_err("%s: failed to register power supply\n", __func__);
+
+	ret = request_threaded_irq(gpio_to_irq(GPIO_OTG_VBUS_SENSE),
+				   NULL, ta_int_intr,
+				   IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+				   IRQF_ONESHOT, "usb_vbus", NULL);
+	if (ret) {
+		pr_err("%s: usb_vbus irq register failed, ret=%d\n",
+		       __func__, ret);
+	} else {
+		ret = enable_irq_wake(gpio_to_irq(GPIO_OTG_VBUS_SENSE));
+		if (ret)
+			pr_warn("%s: failed to enable irq_wake for usb_vbus\n",
+				__func__);
+	}
+
+	ret = request_threaded_irq(gpio_to_irq(GPIO_VBUS_POGO_5V), NULL,
+				   ta_int_intr,
+				   IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+				   IRQF_ONESHOT, "pogo_vbus", NULL);
+	if (ret) {
+		pr_err("%s: pogo_vbus irq register failed, ret=%d\n",
+		       __func__, ret);
+	} else {
+		ret = enable_irq_wake(gpio_to_irq(GPIO_VBUS_POGO_5V));
+		if (ret)
+			pr_warn("%s: failed to enable irq_wake for pogo_vbus\n",
+				__func__);
+	}
+
+	ret = register_pm_notifier(&exynos5_manta_battery_pm_notifier_block);
+	if (ret)
+		pr_warn("%s: failed to register PM notifier; ret=%d\n",
+			__func__, ret);
+
+	usb_xceiv = usb_get_transceiver();
+
+	if (!usb_xceiv) {
+		pr_err("%s: No USB transceiver found\n", __func__);
+	} else {
+		ret = usb_register_notifier(usb_xceiv, &manta_bat_usb_nb);
+
+		if (ret) {
+			pr_err("%s: usb_register_notifier on transceiver %s failed\n",
+			       __func__, dev_name(usb_xceiv->dev));
+		}
+	}
+
+	/* Poll initial charger state */
+	change_charger_status(false, false);
+	return 0;
+}
+
+late_initcall(exynos5_manta_battery_late_init);
diff --git a/arch/arm/mach-exynos/board-manta-bluetooth.c b/arch/arm/mach-exynos/board-manta-bluetooth.c
new file mode 100755
index 0000000..7f8e987
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta-bluetooth.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rfkill.h>
+#include <linux/wakelock.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <asm/mach-types.h>
+#include <mach/gpio.h>
+#include <plat/gpio-cfg.h>
+
+#include "board-manta.h"
+
+#define GPIO_BT_WAKE		EXYNOS5_GPH1(3)
+#define GPIO_BT_HOST_WAKE	EXYNOS5_GPX2(6)
+#define GPIO_BTREG_ON		EXYNOS5_GPH0(0)
+
+#define GPIO_BT_UART_RXD	EXYNOS5_GPA0(0)
+#define GPIO_BT_UART_TXD	EXYNOS5_GPA0(1)
+#define GPIO_BT_UART_CTS	EXYNOS5_GPA0(2)
+#define GPIO_BT_UART_RTS	EXYNOS5_GPA0(3)
+
+#define BT_LPM_ENABLE
+
+static struct rfkill *bt_rfkill;
+
+static DEFINE_MUTEX(manta_bt_wlan_sync);
+
+struct bcm_bt_lpm {
+	int wake;
+	int host_wake;
+
+	struct hrtimer enter_lpm_timer;
+	ktime_t enter_lpm_delay;
+
+	struct uart_port *uport;
+
+	struct wake_lock wake_lock;
+	struct wake_lock host_wake_lock;
+} bt_lpm;
+
+struct gpio_init_data {
+	uint num;
+	uint cfg;
+	uint pull;
+	uint drv;
+};
+
+struct gpio_sleep_data {
+	uint num;
+	uint cfg;
+	uint pull;
+};
+
+static struct gpio_init_data manta_init_bt_gpios[] = {
+	/* BT_UART_RXD */
+	{GPIO_BT_UART_RXD, S3C_GPIO_SFN(2), S3C_GPIO_PULL_UP,
+							S5P_GPIO_DRVSTR_LV2},
+	/* BT_UART_TXD */
+	{GPIO_BT_UART_TXD, S3C_GPIO_SFN(2), S3C_GPIO_PULL_NONE,
+							S5P_GPIO_DRVSTR_LV2},
+	/* BT_UART_CTS */
+	{GPIO_BT_UART_CTS, S3C_GPIO_SFN(2), S3C_GPIO_PULL_NONE,
+							S5P_GPIO_DRVSTR_LV2},
+	/* BT_UART_RTS */
+	{GPIO_BT_UART_RTS, S3C_GPIO_SFN(2), S3C_GPIO_PULL_NONE,
+							S5P_GPIO_DRVSTR_LV2},
+	/* BT_HOST_WAKE */
+	{GPIO_BT_HOST_WAKE, S3C_GPIO_INPUT, S3C_GPIO_PULL_NONE,
+							S5P_GPIO_DRVSTR_LV1}
+};
+
+static struct gpio_sleep_data manta_sleep_bt_gpios[] = {
+	/* BT_UART_RXD */
+	{GPIO_BT_UART_RXD, S5P_GPIO_PD_INPUT, S5P_GPIO_PD_UP_ENABLE},
+	/* BT_UART_TXD */
+	{GPIO_BT_UART_TXD, S5P_GPIO_PD_OUTPUT0, S5P_GPIO_PD_UPDOWN_DISABLE},
+	/* BT_UART_CTS */
+	{GPIO_BT_UART_CTS, S5P_GPIO_PD_INPUT, S5P_GPIO_PD_UPDOWN_DISABLE},
+	/* BT_UART_RTS */
+	{GPIO_BT_UART_RTS, S5P_GPIO_PD_OUTPUT1, S5P_GPIO_PD_UPDOWN_DISABLE},
+	/* BTREG_ON */
+	{GPIO_BTREG_ON, S5P_GPIO_PD_PREV_STATE, S5P_GPIO_PD_UPDOWN_DISABLE},
+	/* BT_WAKE */
+	{GPIO_BT_WAKE, S5P_GPIO_PD_PREV_STATE, S5P_GPIO_PD_UPDOWN_DISABLE},
+};
+
+static struct platform_device bcm43241_bluetooth_platform_device = {
+	.name		= "bcm43241_bluetooth",
+	.id		= -1,
+};
+
+static struct platform_device *manta_bt_devs[] __initdata = {
+	&bcm43241_bluetooth_platform_device,
+};
+
+void __init exynos5_manta_bt_init(void)
+{
+	int i;
+	int gpio;
+
+	for (i = 0; i < ARRAY_SIZE(manta_init_bt_gpios); i++) {
+		gpio = manta_init_bt_gpios[i].num;
+		s3c_gpio_cfgpin(gpio, manta_init_bt_gpios[i].cfg);
+		s3c_gpio_setpull(gpio, manta_init_bt_gpios[i].pull);
+		s5p_gpio_set_drvstr(gpio, manta_init_bt_gpios[i].drv);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(manta_sleep_bt_gpios); i++) {
+		gpio = manta_sleep_bt_gpios[i].num;
+		s5p_gpio_set_pd_cfg(gpio, manta_sleep_bt_gpios[i].cfg);
+		s5p_gpio_set_pd_pull(gpio, manta_sleep_bt_gpios[i].pull);
+	}
+
+	platform_add_devices(manta_bt_devs, ARRAY_SIZE(manta_bt_devs));
+}
+
+void bt_wlan_lock(void)
+{
+	mutex_lock(&manta_bt_wlan_sync);
+}
+
+void bt_wlan_unlock(void)
+{
+	mutex_unlock(&manta_bt_wlan_sync);
+}
+
+static int bcm43241_bt_rfkill_set_power(void *data, bool blocked)
+{
+	/* rfkill_ops callback. Turn transmitter on when blocked is false */
+	bt_wlan_lock();
+	msleep(300);
+	if (!blocked) {
+		pr_info("[BT] Bluetooth Power On.\n");
+		gpio_set_value(GPIO_BTREG_ON, 1);
+		s3c_gpio_setpull(GPIO_BT_HOST_WAKE, S3C_GPIO_PULL_NONE);
+	} else {
+		pr_info("[BT] Bluetooth Power Off.\n");
+		gpio_set_value(GPIO_BTREG_ON, 0);
+		s3c_gpio_setpull(GPIO_BT_HOST_WAKE, S3C_GPIO_PULL_DOWN);
+	}
+	msleep(50);
+	bt_wlan_unlock();
+	return 0;
+}
+
+static const struct rfkill_ops bcm43241_bt_rfkill_ops = {
+	.set_block = bcm43241_bt_rfkill_set_power,
+};
+
+#ifdef BT_LPM_ENABLE
+static void set_wake_locked(int wake)
+{
+	if (wake == bt_lpm.wake)
+		return;
+
+	bt_lpm.wake = wake;
+
+	if (wake) {
+		wake_lock(&bt_lpm.wake_lock);
+		gpio_set_value(GPIO_BT_WAKE, wake);
+	} else {
+		gpio_set_value(GPIO_BT_WAKE, wake);
+		wake_unlock(&bt_lpm.wake_lock);
+	}
+}
+
+static enum hrtimer_restart enter_lpm(struct hrtimer *timer)
+{
+	set_wake_locked(0);
+
+	return HRTIMER_NORESTART;
+}
+
+void bcm_bt_lpm_exit_lpm_locked(struct uart_port *uport)
+{
+	bt_lpm.uport = uport;
+
+	hrtimer_try_to_cancel(&bt_lpm.enter_lpm_timer);
+	set_wake_locked(1);
+
+	hrtimer_start(&bt_lpm.enter_lpm_timer, bt_lpm.enter_lpm_delay,
+		HRTIMER_MODE_REL);
+}
+
+static void update_host_wake_locked(int host_wake)
+{
+	if (host_wake == bt_lpm.host_wake)
+		return;
+
+	bt_lpm.host_wake = host_wake;
+
+	if (host_wake) {
+		wake_lock(&bt_lpm.host_wake_lock);
+	} else  {
+		/* Take a timed wakelock, so that upper layers can take it.
+		* The chipset deasserts the hostwake lock, when there is no
+		* more data to send.
+		*/
+		wake_lock_timeout(&bt_lpm.host_wake_lock, HZ/2);
+	}
+}
+
+static irqreturn_t host_wake_isr(int irq, void *dev)
+{
+	int host_wake;
+
+	host_wake = gpio_get_value(GPIO_BT_HOST_WAKE);
+	irq_set_irq_type(irq, host_wake ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH);
+
+	if (!bt_lpm.uport) {
+		bt_lpm.host_wake = host_wake;
+		return IRQ_HANDLED;
+	}
+
+	update_host_wake_locked(host_wake);
+
+	return IRQ_HANDLED;
+}
+
+static int bcm_bt_lpm_init(struct platform_device *pdev)
+{
+	int irq;
+	int ret;
+
+	pr_info("[BT] bcm_bt_lpm_init\n");
+	hrtimer_init(&bt_lpm.enter_lpm_timer, CLOCK_MONOTONIC,
+			HRTIMER_MODE_REL);
+	bt_lpm.enter_lpm_delay = ktime_set(3, 0);
+	bt_lpm.enter_lpm_timer.function = enter_lpm;
+
+	bt_lpm.host_wake = 0;
+
+	wake_lock_init(&bt_lpm.wake_lock, WAKE_LOCK_SUSPEND,
+			 "BTWakeLowPower");
+	wake_lock_init(&bt_lpm.host_wake_lock, WAKE_LOCK_SUSPEND,
+			 "BTHostWakeLowPower");
+
+	irq = gpio_to_irq(GPIO_BT_HOST_WAKE);
+	ret = request_threaded_irq(irq, NULL, host_wake_isr,
+		IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "bt_host_wake", NULL);
+	if (ret) {
+		pr_err("[BT] Request host_wake irq failed.\n");
+		goto err_lpm_init;
+	}
+
+	ret = irq_set_irq_wake(irq, 1);
+	if (ret) {
+		pr_err("[BT] Set_irq_wake failed.\n");
+		free_irq(irq, NULL);
+		goto err_lpm_init;
+	}
+
+	return 0;
+
+err_lpm_init:
+	wake_lock_destroy(&bt_lpm.wake_lock);
+	wake_lock_destroy(&bt_lpm.host_wake_lock);
+	return ret;
+}
+#endif
+
+static int bcm43241_bluetooth_probe(struct platform_device *pdev)
+{
+	int rc;
+
+	rc = gpio_request(GPIO_BTREG_ON, "bcm43241_bten_gpio");
+	if (unlikely(rc)) {
+		pr_err("[BT] GPIO_BTREG_ON request failed.\n");
+		goto err_gpio_btreg_on;
+	}
+
+	rc = gpio_request(GPIO_BT_WAKE, "bcm43241_btwake_gpio");
+	if (unlikely(rc)) {
+		pr_err("[BT] GPIO_BT_WAKE request failed.\n");
+		goto err_gpio_bt_wake;
+	}
+
+	rc = gpio_request(GPIO_BT_HOST_WAKE, "bcm43241_bthostwake_gpio");
+	if (unlikely(rc)) {
+		pr_err("[BT] GPIO_BT_HOST_WAKE request failed.\n");
+		goto err_gpio_bt_host_wake;
+	}
+
+	gpio_direction_input(GPIO_BT_HOST_WAKE);
+	gpio_direction_output(GPIO_BT_WAKE, 0);
+	gpio_direction_output(GPIO_BTREG_ON, 0);
+
+	bt_rfkill = rfkill_alloc("bcm43241 Bluetooth", &pdev->dev,
+				RFKILL_TYPE_BLUETOOTH, &bcm43241_bt_rfkill_ops,
+				NULL);
+	if (unlikely(!bt_rfkill)) {
+		pr_err("[BT] bt_rfkill alloc failed.\n");
+		rc =  -ENOMEM;
+		goto err_rfkill_alloc;
+	}
+
+	rfkill_init_sw_state(bt_rfkill, false);
+	rc = rfkill_register(bt_rfkill);
+	if (unlikely(rc)) {
+		pr_err("[BT] bt_rfkill register failed.\n");
+		rc = -1;
+		goto err_rfkill_register;
+	}
+	rfkill_set_sw_state(bt_rfkill, true);
+
+#ifdef BT_LPM_ENABLE
+	rc = bcm_bt_lpm_init(pdev);
+	if (rc)
+		goto err_lpm_init;
+#endif
+	return rc;
+
+#ifdef BT_LPM_ENABLE
+err_lpm_init:
+	rfkill_unregister(bt_rfkill);
+#endif
+err_rfkill_register:
+	rfkill_destroy(bt_rfkill);
+err_rfkill_alloc:
+	gpio_free(GPIO_BT_HOST_WAKE);
+err_gpio_bt_host_wake:
+	gpio_free(GPIO_BT_WAKE);
+err_gpio_bt_wake:
+	gpio_free(GPIO_BTREG_ON);
+err_gpio_btreg_on:
+	return rc;
+}
+
+static int bcm43241_bluetooth_remove(struct platform_device *pdev)
+{
+#ifdef BT_LPM_ENABLE
+	int irq;
+
+	irq = gpio_to_irq(GPIO_BT_HOST_WAKE);
+	irq_set_irq_wake(irq, 0);
+	free_irq(irq, NULL);
+	set_wake_locked(0);
+	hrtimer_try_to_cancel(&bt_lpm.enter_lpm_timer);
+	wake_lock_destroy(&bt_lpm.wake_lock);
+	wake_lock_destroy(&bt_lpm.host_wake_lock);
+#endif
+
+	rfkill_unregister(bt_rfkill);
+	rfkill_destroy(bt_rfkill);
+
+	gpio_free(GPIO_BT_HOST_WAKE);
+	gpio_free(GPIO_BT_WAKE);
+	gpio_free(GPIO_BTREG_ON);
+
+	return 0;
+}
+
+static struct platform_driver bcm43241_bluetooth_platform_driver = {
+	.probe = bcm43241_bluetooth_probe,
+	.remove = bcm43241_bluetooth_remove,
+	.driver = {
+		   .name = "bcm43241_bluetooth",
+		   .owner = THIS_MODULE,
+	},
+};
+
+static int __init bcm43241_bluetooth_init(void)
+{
+	return platform_driver_register(&bcm43241_bluetooth_platform_driver);
+}
+
+static void __exit bcm43241_bluetooth_exit(void)
+{
+	platform_driver_unregister(&bcm43241_bluetooth_platform_driver);
+}
+
+
+module_init(bcm43241_bluetooth_init);
+module_exit(bcm43241_bluetooth_exit);
+
+MODULE_ALIAS("platform:bcm43241");
+MODULE_DESCRIPTION("bcm43241_bluetooth");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-exynos/board-manta-camera.c b/arch/arm/mach-exynos/board-manta-camera.c
new file mode 100755
index 0000000..b53b17a
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta-camera.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+
+#include <plat/s3c64xx-spi.h>
+#include <plat/cpu.h>
+#include <plat/devs.h>
+#include <plat/gpio-cfg.h>
+
+#include <mach/spi-clocks.h>
+#include <mach/gpio.h>
+#include <mach/sysmmu.h>
+
+#include <media/exynos_fimc_is.h>
+
+
+static struct exynos5_platform_fimc_is exynos5_fimc_is_data;
+
+#if defined CONFIG_VIDEO_S5K4E5
+static struct exynos5_fimc_is_sensor_info s5k4e5 = {
+	.sensor_name = "S5K4E5",
+	.sensor_id = SENSOR_NAME_S5K4E5,
+#if defined CONFIG_S5K4E5_POSITION_FRONT
+	.sensor_position = SENSOR_POSITION_FRONT,
+#elif defined CONFIG_S5K4E5_POSITION_REAR
+	.sensor_position = SENSOR_POSITION_REAR,
+#endif
+#if defined CONFIG_S5K4E5_CSI_C
+	.csi_id = CSI_ID_A,
+	.flite_id = FLITE_ID_A,
+	.i2c_channel = SENSOR_CONTROL_I2C0,
+#elif defined CONFIG_S5K4E5_CSI_D
+	.csi_id = CSI_ID_B,
+	.flite_id = FLITE_ID_B,
+	.i2c_channel = SENSOR_CONTROL_I2C1,
+#endif
+	.max_width = 2560,
+	.max_height = 1920,
+	.max_frame_rate = 30,
+
+	.mipi_lanes = 2,
+	.mipi_settle = 12,
+	.mipi_align = 24,
+	.sensor_power = {
+		.cam_core = "5m_core_1.5v",
+		.cam_io_myself = "cam_io_1.8v",
+		.cam_io_peer = "vt_cam_1.8v",
+		.cam_af = "cam_af_2.8v",
+	},
+	.sensor_gpio = {
+		.cfg[0] = { /* ISP_TXD */
+			.pin = EXYNOS5_GPE0(7),
+			.name = "GPE0",
+			.value = (3<<28),
+			.act = GPIO_PULL_NONE,
+		},
+		.cfg[1] = { /* ISP_RXD */
+			.pin = EXYNOS5_GPE1(1),
+			.name = "GPE1",
+			.value = (3<<4),
+			.act = GPIO_PULL_NONE,
+		},
+		.cfg[2] = { /* 5M_CAM_SDA_18V */
+			.pin = EXYNOS5_GPF0(0),
+			.name = "GPF0",
+			.value = (2<<0),
+			.act = GPIO_PULL_NONE,
+		},
+		.cfg[3] = { /* 5M_CAM_SCL_18V */
+			.pin = EXYNOS5_GPF0(1),
+			.name = "GPF0",
+			.value = (2<<4),
+			.act = GPIO_PULL_NONE,
+		},
+		.cfg[4] = { /* CAM_FLASH_EN */
+			.pin = EXYNOS5_GPE0(1),
+			.name = "GPE0",
+			.value = (2<<4),
+			.act = GPIO_PULL_NONE,
+		},
+		.cfg[5] = { /* CAM_FLASH_SET */
+			.pin = EXYNOS5_GPE0(2),
+			.name = "GPE0",
+			.value = (2<<8),
+			.act = GPIO_PULL_NONE,
+		},
+		.cfg[6] = { /* CAM_MCLK */
+			.pin = EXYNOS5_GPH0(3),
+			.name = "GPH0",
+			.value = (2<<12),
+			.act = GPIO_PULL_NONE,
+		},
+		.power = { /* CAM_IO_EN - VDDA_2.8V*/
+			.pin = EXYNOS5_GPV0(3),
+			.name = "GPV0",
+			.value = 1,
+			.act = GPIO_OUTPUT,
+		},
+		.reset_myself = { /* 5M_CAM_RESET */
+			.pin = EXYNOS5_GPE0(0),
+			.name = "GPE0",
+			.value = 0,
+			.act = GPIO_RESET,
+		},
+		.reset_peer = { /* CAM_VT_nRST */
+			.pin = EXYNOS5_GPG1(6),
+			.name = "GPG1",
+			.value = 0,
+			.act = GPIO_RESET,
+		},
+	},
+};
+#endif
+
+#if defined CONFIG_VIDEO_S5K6A3
+static struct exynos5_fimc_is_sensor_info s5k6a3 = {
+	.sensor_name = "S5K6A3",
+	.sensor_id = SENSOR_NAME_S5K6A3,
+#if defined CONFIG_S5K6A3_POSITION_FRONT
+	.sensor_position = SENSOR_POSITION_FRONT,
+#elif defined CONFIG_S5K6A3_POSITION_REAR
+	.sensor_position = SENSOR_POSITION_REAR,
+#endif
+#if defined CONFIG_S5K6A3_CSI_C
+	.csi_id = CSI_ID_A,
+	.flite_id = FLITE_ID_A,
+	.i2c_channel = SENSOR_CONTROL_I2C0,
+#elif defined CONFIG_S5K6A3_CSI_D
+	.csi_id = CSI_ID_B,
+	.flite_id = FLITE_ID_B,
+	.i2c_channel = SENSOR_CONTROL_I2C1,
+#endif
+	.max_width = 1280,
+	.max_height = 720,
+	.max_frame_rate = 30,
+
+	.mipi_lanes = 1,
+	.mipi_settle = 12,
+	.mipi_align = 24,
+	.sensor_power = {
+		.cam_core = "5m_core_1.5v",
+		.cam_io_myself = "vt_cam_1.8v",
+		.cam_io_peer = "cam_io_1.8v",
+	},
+	.sensor_gpio = {
+		.cfg[0] = { /* ISP_TXD */
+			.pin = EXYNOS5_GPE0(7),
+			.name = "GPE0",
+			.value = (3<<28),
+			.act = GPIO_PULL_NONE,
+		},
+		.cfg[1] = { /* ISP_RXD */
+			.pin = EXYNOS5_GPE1(1),
+			.name = "GPE1",
+			.value = (3<<4),
+			.act = GPIO_PULL_NONE,
+		},
+		.cfg[2] = { /* VT_CAM_SDA_18V */
+			.pin = EXYNOS5_GPF0(2),
+			.name = "GPF0",
+			.value = (2<<8),
+			.act = GPIO_PULL_NONE,
+		},
+		.cfg[3] = { /* VT_CAM_SCL_18V */
+			.pin = EXYNOS5_GPF0(3),
+			.name = "GPF0",
+			.value = (2<<12),
+			.act = GPIO_PULL_NONE,
+		},
+		.cfg[4] = { /* VTCAM_MCLK */
+			.pin = EXYNOS5_GPG2(1),
+			.name = "GPG2",
+			.value = (2<<4),
+			.act = GPIO_PULL_NONE,
+		},
+		.power = { /* CAM_IO_EN - VDDA_2.8V*/
+			.pin = EXYNOS5_GPV0(3),
+			.name = "GPV0",
+			.value = 1,
+			.act = GPIO_OUTPUT,
+		},
+		.reset_myself = { /* CAM_VT_nRST */
+			.pin = EXYNOS5_GPG1(6),
+			.name = "GPG1",
+			.value = 0,
+			.act = GPIO_RESET,
+		},
+		.reset_peer = { /* 5M_CAM_RESET */
+			.pin = EXYNOS5_GPE0(0),
+			.name = "GPE0",
+			.value = 0,
+			.act = GPIO_RESET,
+		},
+	},
+};
+#endif
+
+static struct s3c64xx_spi_csinfo spi1_csi[] = {
+	[0] = {
+		.line           = EXYNOS5_GPA2(5),
+		.set_level      = gpio_set_value,
+		.fb_delay       = 0x2,
+	},
+};
+
+static struct spi_board_info spi1_board_info[] __initdata = {
+	{
+		.modalias               = "fimc_is_spi",
+		.platform_data          = NULL,
+		.max_speed_hz           = 10 * 1000 * 1000,
+		.bus_num                = 1,
+		.chip_select            = 0,
+		.mode                   = SPI_MODE_0,
+		.controller_data        = &spi1_csi[0],
+	}
+};
+
+static void manta_gpio_pull_up(bool pull_up)
+{
+	if (pull_up) {
+		s3c_gpio_cfgpin(EXYNOS5_GPA2(4), S3C_GPIO_SFN(2));
+		s3c_gpio_cfgpin(EXYNOS5_GPA2(6), S3C_GPIO_SFN(2));
+		s3c_gpio_cfgpin(EXYNOS5_GPA2(7), S3C_GPIO_SFN(2));
+		pr_debug("GPIO : spi function\n");
+	} else {
+		s3c_gpio_setpull(EXYNOS5_GPA2(4), S3C_GPIO_PULL_DOWN);
+		s3c_gpio_setpull(EXYNOS5_GPA2(6), S3C_GPIO_PULL_DOWN);
+		s3c_gpio_setpull(EXYNOS5_GPA2(7), S3C_GPIO_PULL_DOWN);
+		s3c_gpio_cfgpin(EXYNOS5_GPA2(4), S3C_GPIO_SFN(0));
+		s3c_gpio_cfgpin(EXYNOS5_GPA2(6), S3C_GPIO_SFN(0));
+		s3c_gpio_cfgpin(EXYNOS5_GPA2(7), S3C_GPIO_SFN(0));
+		pr_debug("GPIO : input\n");
+	}
+}
+
+static struct platform_device *camera_devices[] __initdata = {
+	&s3c64xx_device_spi1,
+	&exynos5_device_fimc_is,
+};
+
+static void __init manta_camera_sysmmu_init(void)
+{
+	platform_set_sysmmu(&SYSMMU_PLATDEV(isp).dev,
+					&exynos5_device_fimc_is.dev);
+
+}
+
+void __init exynos5_manta_camera_init(void)
+{
+	manta_camera_sysmmu_init();
+	platform_add_devices(camera_devices, ARRAY_SIZE(camera_devices));
+
+	/* SPI */
+	exynos_spi_clock_setup(&s3c64xx_device_spi1.dev, 1);
+
+	if (!exynos_spi_cfg_cs(spi1_csi[0].line, 1)) {
+		s3c64xx_spi1_pdata.gpio_pull_up = manta_gpio_pull_up;
+		s3c64xx_spi1_set_platdata(&s3c64xx_spi1_pdata,
+			EXYNOS_SPI_SRCCLK_SCLK, ARRAY_SIZE(spi1_csi));
+
+		spi_register_board_info(spi1_board_info,
+			ARRAY_SIZE(spi1_board_info));
+	} else {
+		pr_err("%s: Error requesting gpio for SPI-CH1 CS\n", __func__);
+	}
+
+	/* FIMC-IS-MC */
+	dev_set_name(&exynos5_device_fimc_is.dev, "s5p-mipi-csis.0");
+	clk_add_alias("gscl_wrap0", FIMC_IS_MODULE_NAME, "gscl_wrap0",
+			&exynos5_device_fimc_is.dev);
+	clk_add_alias("sclk_gscl_wrap0", FIMC_IS_MODULE_NAME, "sclk_gscl_wrap0",
+			&exynos5_device_fimc_is.dev);
+
+	dev_set_name(&exynos5_device_fimc_is.dev, "s5p-mipi-csis.1");
+	clk_add_alias("gscl_wrap1", FIMC_IS_MODULE_NAME, "gscl_wrap1",
+			&exynos5_device_fimc_is.dev);
+	clk_add_alias("sclk_gscl_wrap1", FIMC_IS_MODULE_NAME, "sclk_gscl_wrap1",
+			&exynos5_device_fimc_is.dev);
+
+	dev_set_name(&exynos5_device_fimc_is.dev, "exynos-gsc.0");
+	clk_add_alias("gscl", FIMC_IS_MODULE_NAME, "gscl",
+			&exynos5_device_fimc_is.dev);
+	dev_set_name(&exynos5_device_fimc_is.dev, FIMC_IS_MODULE_NAME);
+
+#if defined CONFIG_VIDEO_S5K6A3
+	exynos5_fimc_is_data.sensor_info[s5k6a3.sensor_position] = &s5k6a3;
+#endif
+#if defined CONFIG_VIDEO_S5K4E5
+	exynos5_fimc_is_data.sensor_info[s5k4e5.sensor_position] = &s5k4e5;
+#endif
+
+	exynos5_fimc_is_set_platdata(&exynos5_fimc_is_data);
+}
+
diff --git a/arch/arm/mach-exynos/board-manta-connector.c b/arch/arm/mach-exynos/board-manta-connector.c
new file mode 100644
index 0000000..87ed671
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta-connector.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) "manta_otg %s: " fmt, __func__
+
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/otg.h>
+
+#include <plat/gpio-cfg.h>
+#include <plat/devs.h>
+#include <plat/usb-phy.h>
+
+#include "board-manta.h"
+
+#define GPIO_USB_VBUS	EXYNOS5_GPX1(0)
+#define GPIO_USB_ID	EXYNOS5_GPX1(1)
+
+struct manta_otg {
+	struct usb_otg		otg;
+	struct usb_phy		phy;
+	struct delayed_work	work;
+	struct mutex		lock;
+	bool			usb_connected;
+	struct usb_bus		*ohci;
+
+	/* HACK: s5p phy interface requires passing a pdev pointer */
+	struct platform_device	pdev;
+};
+static struct manta_otg manta_otg;
+
+
+static int manta_phy_init(struct usb_phy *phy)
+{
+	struct manta_otg *motg = container_of(phy, struct manta_otg, phy);
+
+	if (phy->last_event == USB_EVENT_VBUS)
+		return s5p_usb_phy_init(&motg->pdev, S5P_USB_PHY_DEVICE);
+	else
+		return s5p_usb_phy_init(&motg->pdev, S5P_USB_PHY_HOST);
+}
+
+static void manta_phy_shutdown(struct usb_phy *phy)
+{
+	struct manta_otg *motg = container_of(phy, struct manta_otg, phy);
+
+	if (motg->phy.state == OTG_STATE_B_PERIPHERAL)
+		s5p_usb_phy_exit(&motg->pdev, S5P_USB_PHY_DEVICE);
+	else
+		s5p_usb_phy_exit(&motg->pdev, S5P_USB_PHY_HOST);
+}
+
+static int manta_phy_set_power(struct usb_phy *phy, unsigned mA)
+{
+	if (mA > 3)
+		atomic_notifier_call_chain(&phy->notifier, USB_EVENT_ENUMERATED,
+					   phy->otg->gadget);
+
+	return 0;
+}
+
+static int manta_otg_host_enable(struct manta_otg *motg)
+{
+#ifdef CONFIG_USB
+	struct usb_hcd *hcd;
+	struct usb_hcd *ohci_hcd = NULL;
+	int err;
+
+	hcd = bus_to_hcd(motg->otg.host);
+	if (motg->ohci)
+		ohci_hcd = bus_to_hcd(motg->ohci);
+
+	usb_phy_init(&motg->phy);
+
+	err = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+	if (err) {
+		pr_err("failed to add ehci: %d\n", err);
+		goto err_ehci;
+	}
+
+	if (ohci_hcd) {
+		err = usb_add_hcd(ohci_hcd, hcd->irq, IRQF_SHARED);
+		if (err) {
+			pr_err("failed to add ohci: %d\n", err);
+			goto err_ohci;
+		}
+	}
+
+	err = otg_set_vbus(&motg->otg, true);
+	if (err) {
+		pr_err("failed to enable vbus: %d\n", err);
+		goto err_vbus;
+	}
+
+	return 0;
+
+err_vbus:
+	if (ohci_hcd)
+		usb_remove_hcd(ohci_hcd);
+
+err_ohci:
+	usb_remove_hcd(hcd);
+
+err_ehci:
+	usb_phy_shutdown(&motg->phy);
+
+	return err;
+#else
+	return 0;
+#endif
+}
+
+static void manta_otg_host_disable(struct manta_otg *motg)
+{
+#ifdef CONFIG_USB
+	struct usb_hcd *hcd;
+	struct usb_hcd *ohci_hcd = NULL;
+	int err;
+
+	hcd = bus_to_hcd(motg->otg.host);
+	if (motg->ohci)
+		ohci_hcd = bus_to_hcd(motg->ohci);
+
+	err = otg_set_vbus(&motg->otg, false);
+	if (err)
+		pr_err("failed to disable vbus: %d\n", err);
+
+	if (ohci_hcd)
+		usb_remove_hcd(ohci_hcd);
+	usb_remove_hcd(hcd);
+
+	usb_phy_shutdown(&motg->phy);
+#endif
+}
+
+static int manta_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+#ifdef CONFIG_USB
+	struct manta_otg *motg = container_of(otg, struct manta_otg, otg);
+	struct usb_hcd *hcd = bus_to_hcd(host);
+
+	mutex_lock(&motg->lock);
+
+	/* HACK to support for ohci */
+	if (host && otg->host) {
+		motg->ohci = host;
+		usb_remove_hcd(hcd);
+		usb_phy_shutdown(&motg->phy);
+		goto out;
+	}
+
+	otg->host = host;
+	if (host) {
+		usb_remove_hcd(bus_to_hcd(host));
+		usb_phy_shutdown(&motg->phy);
+	} else {
+		if (otg->phy->state == OTG_STATE_A_HOST)
+			otg->phy->state = OTG_STATE_UNDEFINED;
+	}
+
+out:
+	mutex_unlock(&motg->lock);
+#endif
+	return 0;
+}
+
+static int manta_otg_set_peripheral(struct usb_otg *otg,
+				    struct usb_gadget *gadget)
+{
+	struct manta_otg *motg = container_of(otg, struct manta_otg, otg);
+
+	mutex_lock(&motg->lock);
+
+	otg->gadget = gadget;
+	if (gadget) {
+		if (otg->phy->state == OTG_STATE_B_PERIPHERAL)
+			usb_gadget_vbus_connect(motg->otg.gadget);
+	} else {
+		if (otg->phy->state == OTG_STATE_B_PERIPHERAL)
+			otg->phy->state = OTG_STATE_UNDEFINED;
+	}
+
+	mutex_unlock(&motg->lock);
+
+	return 0;
+}
+
+static int manta_otg_set_vbus(struct usb_otg *otg, bool enabled)
+{
+	pr_debug("vbus %s\n", enabled ? "on" : "off");
+	return manta_bat_otg_enable(enabled);
+}
+
+static void manta_otg_work(struct work_struct *work)
+{
+	struct manta_otg *motg = container_of(work, struct manta_otg, work.work);
+	enum usb_otg_state prev_state;
+	int id, vbus, err;
+
+	mutex_lock(&motg->lock);
+
+	prev_state = motg->phy.state;
+	vbus = motg->usb_connected;
+	id = gpio_get_value(GPIO_USB_ID);
+
+	pr_debug("vbus=%d id=%d\n", vbus, id);
+
+	if (!id) {
+		if (prev_state == OTG_STATE_A_HOST)
+			goto out;
+
+		if (!motg->otg.host)
+			goto out;
+
+		motg->phy.state = OTG_STATE_A_HOST;
+		motg->phy.last_event = USB_EVENT_ID;
+		atomic_notifier_call_chain(&motg->phy.notifier,
+					USB_EVENT_ID, NULL);
+
+		err = manta_otg_host_enable(motg);
+		if (err) {
+			motg->phy.last_event = USB_EVENT_NONE;
+			motg->phy.state = OTG_STATE_B_IDLE;
+			atomic_notifier_call_chain(&motg->phy.notifier,
+						USB_EVENT_NONE, NULL);
+			goto out;
+		}
+
+	} else if (vbus) {
+		if (!motg->otg.gadget)
+			goto out;
+
+		if (prev_state == OTG_STATE_B_PERIPHERAL)
+			goto out;
+
+		motg->phy.state = OTG_STATE_B_PERIPHERAL;
+		motg->phy.last_event = USB_EVENT_VBUS;
+		atomic_notifier_call_chain(&motg->phy.notifier,
+					USB_EVENT_VBUS, NULL);
+		usb_gadget_vbus_connect(motg->otg.gadget);
+	} else {
+		if (prev_state == OTG_STATE_B_IDLE)
+			goto out;
+
+		if (prev_state == OTG_STATE_B_PERIPHERAL && motg->otg.gadget)
+			usb_gadget_vbus_disconnect(motg->otg.gadget);
+
+		if (prev_state == OTG_STATE_A_HOST && motg->otg.host)
+			manta_otg_host_disable(motg);
+
+		motg->phy.state = OTG_STATE_B_IDLE;
+		motg->phy.last_event = USB_EVENT_NONE;
+		atomic_notifier_call_chain(&motg->phy.notifier,
+					USB_EVENT_NONE, NULL);
+	}
+
+	pr_info("%s -> %s\n", otg_state_string(prev_state),
+			      otg_state_string(motg->phy.state));
+
+out:
+	mutex_unlock(&motg->lock);
+}
+
+static irqreturn_t manta_otg_irq(int irq, void *data)
+{
+	struct manta_otg *motg = data;
+	queue_delayed_work(system_nrt_wq, &motg->work, msecs_to_jiffies(20));
+	return IRQ_HANDLED;
+}
+
+void manta_otg_set_usb_state(bool connected)
+{
+	struct manta_otg *motg = &manta_otg;
+	motg->usb_connected = connected;
+	queue_delayed_work(system_nrt_wq, &motg->work,
+			   connected ? msecs_to_jiffies(20) : 0);
+	if (!connected)
+		flush_delayed_work(&motg->work);
+}
+
+void exynos5_manta_connector_init(void)
+{
+	struct manta_otg *motg = &manta_otg;
+	struct device *dev = &motg->pdev.dev;
+
+	INIT_DELAYED_WORK(&motg->work, manta_otg_work);
+	ATOMIC_INIT_NOTIFIER_HEAD(&motg->phy.notifier);
+	mutex_init(&motg->lock);
+
+	device_initialize(dev);
+	dev_set_name(dev, "%s", "manta_otg");
+	if (device_add(dev)) {
+		dev_err(dev, "%s: cannot reg device\n", __func__);
+		return;
+	}
+	dev_set_drvdata(dev, motg);
+
+	motg->phy.dev		= dev;
+	motg->phy.label		= "manta_otg";
+	motg->phy.otg		= &motg->otg;
+	motg->phy.init		= manta_phy_init;
+	motg->phy.shutdown	= manta_phy_shutdown;
+	motg->phy.set_power	= manta_phy_set_power;
+
+	motg->otg.phy		 = &motg->phy;
+	motg->otg.set_host	 = manta_otg_set_host;
+	motg->otg.set_peripheral = manta_otg_set_peripheral;
+	motg->otg.set_vbus	 = manta_otg_set_vbus;
+
+	usb_set_transceiver(&motg->phy);
+}
+
+static int __init manta_connector_init(void)
+{
+	struct manta_otg *motg = &manta_otg;
+	int ret;
+
+	s3c_gpio_cfgpin(GPIO_USB_ID, S3C_GPIO_INPUT);
+	s3c_gpio_setpull(GPIO_USB_ID, S3C_GPIO_PULL_NONE);
+
+	ret = request_irq(gpio_to_irq(GPIO_USB_ID), manta_otg_irq,
+			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+			IRQF_ONESHOT, "usb_id", motg);
+	if (ret)
+		pr_err("request_irq ID failed: %d\n", ret);
+
+	/*
+	 * HACK: delay the initial otg detection by 4 secs so that it happens
+	 * after the battery driver is ready (this fixes booting with otg cable
+	 * attached)
+	 */
+	queue_delayed_work(system_nrt_wq, &motg->work, msecs_to_jiffies(4000));
+
+	return ret;
+}
+device_initcall(manta_connector_init);
diff --git a/arch/arm/mach-exynos/board-manta-display.c b/arch/arm/mach-exynos/board-manta-display.c
new file mode 100644
index 0000000..d98233c
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta-display.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/fb.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+
+#include <video/platform_lcd.h>
+#include <video/s5p-dp.h>
+
+#include <plat/backlight.h>
+#include <plat/clock.h>
+#include <plat/devs.h>
+#include <plat/dp.h>
+#include <plat/fb.h>
+#include <plat/gpio-cfg.h>
+#include <plat/iic.h>
+#include <plat/regs-fb-v4.h>
+#include <plat/tv-core.h>
+
+#include <mach/gpio.h>
+#include <mach/map.h>
+#include <mach/regs-clock.h>
+#include <mach/sysmmu.h>
+
+#include "common.h"
+
+#define GPIO_LCD_EN		EXYNOS5_GPH1(7)
+#define GPIO_LCD_ID		EXYNOS5_GPD1(4)
+#define GPIO_LCD_PWM_IN_18V	EXYNOS5_GPB2(0)
+#define GPIO_LED_BL_RST		EXYNOS5_GPG0(5)
+
+#define GPIO_LCDP_SCL__18V	EXYNOS5_GPD0(6)
+#define GPIO_LCDP_SDA__18V	EXYNOS5_GPD0(7)
+
+#define GPIO_HDMI_HPD		EXYNOS5_GPX3(7)
+#define GPIO_HDMI_DCDC_EN	EXYNOS5_GPA0(4)
+#define GPIO_HDMI_LS_EN		EXYNOS5_GPG0(7)
+#define GPIO_APS_EN_18V		EXYNOS5_GPH1(6)
+
+#define LCD_POWER_OFF_TIME_US   (500 * USEC_PER_MSEC)
+
+extern phys_addr_t manta_bootloader_fb_start;
+extern phys_addr_t manta_bootloader_fb_size;
+
+static ktime_t lcd_on_time;
+
+static void manta_lcd_on(void)
+{
+	s64 us = ktime_us_delta(lcd_on_time, ktime_get_boottime());
+	if (us > LCD_POWER_OFF_TIME_US) {
+		pr_warn("lcd on sleep time too long\n");
+		us = LCD_POWER_OFF_TIME_US;
+	}
+
+	if (us > 0)
+		usleep_range(us, us);
+
+	gpio_set_value(GPIO_LCD_EN, 1);
+	usleep_range(200000, 200000);
+}
+
+static void manta_lcd_off(void)
+{
+	gpio_set_value(GPIO_LCD_EN, 0);
+
+	lcd_on_time = ktime_add_us(ktime_get_boottime(), LCD_POWER_OFF_TIME_US);
+}
+
+static void manta_backlight_on(void)
+{
+	usleep_range(97000, 97000);
+	gpio_set_value(GPIO_LED_BL_RST, 1);
+}
+
+static void manta_backlight_off(void)
+{
+	/* LED_BACKLIGHT_RESET: XCI1RGB_5 => GPG0_5 */
+	gpio_set_value(GPIO_LED_BL_RST, 0);
+}
+
+static void manta_lcd_set_power(struct plat_lcd_data *pd,
+				unsigned int power)
+{
+	if (power)
+		manta_lcd_on();
+	else
+		manta_lcd_off();
+}
+
+static struct plat_lcd_data manta_lcd_data = {
+	.set_power	= manta_lcd_set_power,
+};
+
+static struct platform_device manta_lcd = {
+	.name	= "platform-lcd",
+	.dev	= {
+		.parent		= &s5p_device_fimd1.dev,
+		.platform_data	= &manta_lcd_data,
+	},
+};
+
+static void manta_fimd_gpio_setup_24bpp(void)
+{
+	u32 reg;
+
+	/* basic fimd init */
+	exynos5_fimd1_gpio_setup_24bpp();
+
+	/* Reference clcok selection for DPTX_PHY: pad_osc_clk_24M */
+	reg = __raw_readl(S3C_VA_SYS + 0x04d4);
+	reg = (reg & ~(0x1 << 0)) | (0x0 << 0);
+	__raw_writel(reg, S3C_VA_SYS + 0x04d4);
+
+	/* DPTX_PHY: XXTI */
+	reg = __raw_readl(S3C_VA_SYS + 0x04d8);
+	reg = (reg & ~(0x1 << 3)) | (0x0 << 3);
+	__raw_writel(reg, S3C_VA_SYS + 0x04d8);
+}
+
+static struct s3c_fb_pd_win manta_fb_win2 = {
+	.win_mode = {
+		.left_margin	= 80,
+		.right_margin	= 48,
+		.upper_margin	= 37,
+		.lower_margin	= 3,
+		.hsync_len	= 32,
+		.vsync_len	= 6,
+		.xres		= 2560,
+		.yres		= 1600,
+	},
+	.virtual_x		= 2560,
+	.virtual_y		= 1600 * 2,
+	.max_bpp		= 32,
+	.default_bpp		= 24,
+	.width			= 218,
+	.height			= 136,
+};
+
+static struct s3c_fb_platdata manta_lcd1_pdata __initdata = {
+	.win[0]		= &manta_fb_win2,
+	.win[1]		= &manta_fb_win2,
+	.win[2]		= &manta_fb_win2,
+	.win[3]		= &manta_fb_win2,
+	.win[4]		= &manta_fb_win2,
+	.default_win	= 0,
+	.vidcon0	= VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
+	.vidcon1	= 0,
+	.setup_gpio	= manta_fimd_gpio_setup_24bpp,
+	.backlight_off  = manta_backlight_off,
+	.lcd_off	= manta_lcd_off,
+};
+
+static struct video_info manta_dp_config = {
+	.name			= "WQXGA(2560x1600) LCD",
+
+	.h_sync_polarity	= 0,
+	.v_sync_polarity	= 0,
+	.interlaced		= 0,
+
+	.color_space		= COLOR_RGB,
+	.dynamic_range		= VESA,
+	.ycbcr_coeff		= COLOR_YCBCR601,
+	.color_depth		= COLOR_8,
+
+	.link_rate		= LINK_RATE_2_70GBPS,
+	.lane_count		= LANE_COUNT4,
+};
+
+static struct s5p_dp_platdata manta_dp_data __initdata = {
+	.video_info     = &manta_dp_config,
+	.phy_init       = s5p_dp_phy_init,
+	.phy_exit       = s5p_dp_phy_exit,
+	.backlight_on   = manta_backlight_on,
+	.backlight_off  = manta_backlight_off,
+	.lcd_on		= manta_lcd_on,
+	.lcd_off	= manta_lcd_off,
+};
+
+/* LCD Backlight data */
+static struct samsung_bl_gpio_info manta_bl_gpio_info = {
+	.no	= GPIO_LCD_PWM_IN_18V,
+	.func	= S3C_GPIO_SFN(2),
+};
+
+static struct platform_pwm_backlight_data manta_bl_data = {
+	.pwm_id		= 0,
+	.pwm_period_ns	= 1000000,
+	.dft_brightness = 102,
+};
+
+static struct platform_device exynos_device_md0 = {
+	.name = "exynos-mdev",
+	.id = 0,
+};
+
+static struct platform_device exynos_device_md1 = {
+	.name = "exynos-mdev",
+	.id = 1,
+};
+
+static struct platform_device exynos_device_md2 = {
+	.name = "exynos-mdev",
+	.id = 2,
+};
+
+/* HDMI */
+static void manta_hdmiphy_enable(struct platform_device *pdev, int en)
+{
+	s5p_hdmiphy_enable(pdev, en ? 1 : 0);
+}
+
+static struct s5p_hdmi_platdata hdmi_platdata __initdata = {
+	.hdmiphy_enable = manta_hdmiphy_enable,
+};
+
+static struct platform_device *manta_display_devices[] __initdata = {
+	&exynos_device_md0,
+	&exynos_device_md1,
+	&exynos_device_md2,
+
+	&s5p_device_fimd1,
+	&manta_lcd,
+	&s5p_device_dp,
+
+	&s5p_device_i2c_hdmiphy,
+	&s5p_device_mixer,
+	&s5p_device_hdmi,
+};
+
+void __init exynos5_manta_display_init(void)
+{
+	struct resource *res;
+
+	/* LCD_EN , XMMC2CDN => GPC2_2 */
+	gpio_request_one(GPIO_LCD_EN, GPIOF_OUT_INIT_LOW, "LCD_EN");
+	/* LED_BACKLIGHT_RESET: XCI1RGB_5 => GPG0_5 */
+	gpio_request_one(GPIO_LED_BL_RST, GPIOF_OUT_INIT_LOW, "LED_BL_RST");
+	s5p_gpio_set_pd_cfg(GPIO_LED_BL_RST, S5P_GPIO_PD_PREV_STATE);
+	s5p_gpio_set_pd_pull(GPIO_LED_BL_RST, S5P_GPIO_PD_UPDOWN_DISABLE);
+
+	gpio_request_one(GPIO_LCD_PWM_IN_18V, GPIOF_OUT_INIT_LOW, "PWM_IN_18V");
+	s5p_gpio_set_pd_cfg(GPIO_LCD_PWM_IN_18V, S5P_GPIO_PD_INPUT);
+	s5p_gpio_set_pd_pull(GPIO_LCD_PWM_IN_18V, S5P_GPIO_PD_UP_ENABLE);
+	gpio_free(GPIO_LCD_PWM_IN_18V);
+
+	gpio_request_one(GPIO_APS_EN_18V, GPIOF_OUT_INIT_LOW, "APS_EN_18V");
+	s5p_gpio_set_pd_cfg(GPIO_APS_EN_18V, S5P_GPIO_PD_INPUT);
+	s5p_gpio_set_pd_pull(GPIO_APS_EN_18V, S5P_GPIO_PD_UP_ENABLE);
+	gpio_export(GPIO_APS_EN_18V, true);
+
+	samsung_bl_set(&manta_bl_gpio_info, &manta_bl_data);
+	s5p_fimd1_set_platdata(&manta_lcd1_pdata);
+	dev_set_name(&s5p_device_fimd1.dev, "exynos5-fb.1");
+	clk_add_alias("lcd", "exynos5-fb.1", "fimd", &s5p_device_fimd1.dev);
+	s5p_dp_set_platdata(&manta_dp_data);
+
+	gpio_request_one(GPIO_HDMI_HPD, GPIOF_IN, "HDMI_HPD");
+	/* HDMI Companion DC/DC converter and HPD circuitry */
+	gpio_request_one(GPIO_HDMI_DCDC_EN, GPIOF_OUT_INIT_HIGH, "HDMI_DCDC_EN");
+	/* HDMI Companion level shifters and LDO */
+	gpio_request_one(GPIO_HDMI_LS_EN, GPIOF_OUT_INIT_HIGH, "HDMI_LS_EN");
+
+	s5p_hdmi_set_platdata(&hdmi_platdata);
+	dev_set_name(&s5p_device_hdmi.dev, "exynos5-hdmi");
+	clk_add_alias("hdmi", "s5p-hdmi", "hdmi", &s5p_device_hdmi.dev);
+	platform_set_sysmmu(&SYSMMU_PLATDEV(tv).dev, &s5p_device_mixer.dev);
+	s5p_i2c_hdmiphy_set_platdata(NULL);
+
+	platform_add_devices(manta_display_devices,
+			     ARRAY_SIZE(manta_display_devices));
+
+	exynos5_fimd1_setup_clock(&s5p_device_fimd1.dev,
+				"sclk_fimd", "sclk_vpll", 268 * MHZ);
+
+	res = platform_get_resource(&s5p_device_fimd1, IORESOURCE_MEM, 1);
+	if (res) {
+		res->start = manta_bootloader_fb_start;
+		res->end = res->start + manta_bootloader_fb_size - 1;
+		pr_info("bootloader fb located at %8X-%8X\n", res->start,
+				res->end);
+	} else {
+		pr_err("failed to find bootloader fb resource\n");
+	}
+}
diff --git a/arch/arm/mach-exynos/board-manta-gps.c b/arch/arm/mach-exynos/board-manta-gps.c
new file mode 100644
index 0000000..d401709
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta-gps.c
@@ -0,0 +1,68 @@
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <mach/gpio.h>
+#include <plat/gpio-cfg.h>
+
+static struct device *gps_dev;
+
+static struct class *gps_class;
+
+#define GPIO_GPS_PWR_EN  EXYNOS5_GPE1(0)
+#define GPIO_GPS_nRST	 EXYNOS5_GPE0(6)
+#define GPIO_GPS_CTS	EXYNOS5_GPD0(2)
+#define GPIO_GPS_RTS	EXYNOS5_GPD0(3)
+#define GPIO_GPS_RXD	EXYNOS5_GPD0(0)
+#define GPIO_GPS_TXD	EXYNOS5_GPD0(1)
+
+#define GPIO_GPS_CTS_AF   2
+#define GPIO_GPS_RTS_AF   2
+#define GPIO_GPS_RXD_AF   2
+#define GPIO_GPS_TXD_AF   2
+
+void __init exynos5_manta_gps_init(void)
+{
+	int n_rst_pin = 0;
+	int n_rst_nc_pin = 0;
+	gps_class = class_create(THIS_MODULE, "gps");
+	if (IS_ERR(gps_class)) {
+		pr_err("Failed to create class(sec)!\n");
+		return;
+	}
+	BUG_ON(!gps_class);
+	gps_dev = device_create(gps_class, NULL, 0, NULL, "bcm475x");
+	BUG_ON(!gps_dev);
+	s3c_gpio_cfgpin(GPIO_GPS_RXD, S3C_GPIO_SFN(GPIO_GPS_RXD_AF));
+	s3c_gpio_setpull(GPIO_GPS_RXD, S3C_GPIO_PULL_UP);
+	s3c_gpio_cfgpin(GPIO_GPS_TXD, S3C_GPIO_SFN(GPIO_GPS_TXD_AF));
+	s3c_gpio_setpull(GPIO_GPS_TXD, S3C_GPIO_PULL_NONE);
+	s3c_gpio_cfgpin(GPIO_GPS_CTS, S3C_GPIO_SFN(GPIO_GPS_CTS_AF));
+	s3c_gpio_setpull(GPIO_GPS_CTS, S3C_GPIO_PULL_NONE);
+	s3c_gpio_cfgpin(GPIO_GPS_RTS, S3C_GPIO_SFN(GPIO_GPS_RTS_AF));
+	s3c_gpio_setpull(GPIO_GPS_RTS, S3C_GPIO_PULL_NONE);
+
+	n_rst_pin = GPIO_GPS_nRST;
+	n_rst_nc_pin = 0;
+
+	if (gpio_request(n_rst_pin, "GPS_nRST"))
+		WARN(1, "fail to request gpio (GPS_nRST)\n");
+
+	s3c_gpio_setpull(n_rst_pin, S3C_GPIO_PULL_UP);
+	s3c_gpio_cfgpin(n_rst_pin, S3C_GPIO_OUTPUT);
+	gpio_direction_output(n_rst_pin, 1);
+
+	if (gpio_request(GPIO_GPS_PWR_EN, "GPS_PWR_EN"))
+		WARN(1, "fail to request gpio (GPS_PWR_EN)\n");
+
+	s3c_gpio_setpull(GPIO_GPS_PWR_EN, S3C_GPIO_PULL_NONE);
+	s3c_gpio_cfgpin(GPIO_GPS_PWR_EN, S3C_GPIO_OUTPUT);
+	gpio_direction_output(GPIO_GPS_PWR_EN, 0);
+	gpio_export(n_rst_pin, 1);
+	gpio_export(GPIO_GPS_PWR_EN, 1);
+
+	gpio_export_link(gps_dev, "GPS_nRST", n_rst_pin);
+	gpio_export_link(gps_dev, "GPS_PWR_EN", GPIO_GPS_PWR_EN);
+}
diff --git a/arch/arm/mach-exynos/board-manta-input.c b/arch/arm/mach-exynos/board-manta-input.c
new file mode 100644
index 0000000..fa0defe
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta-input.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/i2c/atmel_mxt_ts.h>
+#include <linux/interrupt.h>
+
+#include <plat/devs.h>
+#include <plat/iic.h>
+#include <plat/gpio-cfg.h>
+
+#define GPIO_TOUCH_CHG		EXYNOS5_GPG1(2)
+#define GPIO_TOUCH_RESET	EXYNOS5_GPG1(3)
+#define GPIO_TOUCH_EN_BOOSTER	EXYNOS5_GPD1(1)
+#define GPIO_TOUCH_EN_XVDD	EXYNOS5_GPG0(1)
+
+static struct mxt_platform_data atmel_mxt_ts_pdata = {
+	.x_line         = 32,
+	.y_line         = 52,
+	.x_size         = 4095,
+	.y_size         = 4095,
+	.orient         = MXT_DIAGONAL,
+	.irqflags       = IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+	.boot_address   = 0x26,
+	.gpio_reset     = GPIO_TOUCH_RESET,
+	.reset_msec     = 75,
+};
+
+static struct i2c_board_info i2c_devs3[] __initdata = {
+	{
+		I2C_BOARD_INFO("atmel_mxt_ts", 0x4a),
+		.platform_data	= &atmel_mxt_ts_pdata,
+	},
+};
+
+void __init exynos5_manta_input_init(void)
+{
+	int hw_rev;
+
+	gpio_request(GPIO_TOUCH_CHG, "TSP_INT");
+	s3c_gpio_cfgpin(GPIO_TOUCH_CHG, S3C_GPIO_SFN(0xf));
+	s3c_gpio_setpull(GPIO_TOUCH_CHG, S3C_GPIO_PULL_NONE);
+	s5p_register_gpio_interrupt(GPIO_TOUCH_CHG);
+
+	s5p_gpio_set_pd_cfg(GPIO_TOUCH_CHG, S5P_GPIO_PD_PREV_STATE);
+	s5p_gpio_set_pd_pull(GPIO_TOUCH_CHG, S5P_GPIO_PD_UPDOWN_DISABLE);
+	s5p_gpio_set_pd_cfg(GPIO_TOUCH_RESET, S5P_GPIO_PD_PREV_STATE);
+	s5p_gpio_set_pd_cfg(GPIO_TOUCH_EN_XVDD, S5P_GPIO_PD_PREV_STATE);
+	s5p_gpio_set_pd_cfg(GPIO_TOUCH_EN_BOOSTER, S5P_GPIO_PD_PREV_STATE);
+
+	i2c_devs3[0].irq = gpio_to_irq(GPIO_TOUCH_CHG);
+	i2c_register_board_info(3, i2c_devs3, ARRAY_SIZE(i2c_devs3));
+}
diff --git a/arch/arm/mach-exynos/board-manta-jack.c b/arch/arm/mach-exynos/board-manta-jack.c
new file mode 100644
index 0000000..5b2db67
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta-jack.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/sec_jack.h>
+
+#include <plat/gpio-cfg.h>
+
+#include "board-manta.h"
+
+#define GPIO_DET_35		EXYNOS5_GPX0(1)
+
+static void sec_jack_set_micbias_state(bool on)
+{
+}
+
+static struct sec_jack_zone sec_jack_zones[] = {
+	{
+		/* adc < 50, unstable zone, default to 3pole if it stays
+		* in this range for a half second (20ms delays, 25 samples)
+		*/
+		.adc_high = 50,
+		.delay_ms = 20,
+		.check_count = 25,
+		.jack_type = SEC_HEADSET_3POLE,
+	},
+	{
+		/* 50 < adc <= 490, unstable zone, default to 3pole if it stays
+		* in this range for a second (10ms delays, 100 samples)
+		*/
+		.adc_high = 490,
+		.delay_ms = 10,
+		.check_count = 100,
+		.jack_type = SEC_HEADSET_3POLE,
+	},
+	{
+		/* 490 < adc <= 900, unstable zone, default to 4pole if it
+		* stays in this range for a second (10ms delays, 100 samples)
+		*/
+		.adc_high = 900,
+		.delay_ms = 10,
+		.check_count = 100,
+		.jack_type = SEC_HEADSET_4POLE,
+	},
+	{
+		/* 900 < adc <= 1500, 4 pole zone, default to 4pole if it
+		* stays in this range for 200ms (20ms delays, 10 samples)
+		*/
+		.adc_high = 1500,
+		.delay_ms = 20,
+		.check_count = 10,
+		.jack_type = SEC_HEADSET_4POLE,
+	},
+	{
+		/* adc > 1500, unstable zone, default to 3pole if it stays
+		* in this range for a second (10ms delays, 100 samples)
+		*/
+		.adc_high = 0x7fffffff,
+		.delay_ms = 10,
+		.check_count = 100,
+		.jack_type = SEC_HEADSET_3POLE,
+	},
+};
+
+/* To support 3-buttons earjack */
+static struct sec_jack_buttons_zone sec_jack_buttons_zones[] = {
+	{
+		/* 0 <= adc <= 93, stable zone */
+		.code           = KEY_MEDIA,
+		.adc_low        = 0,
+		.adc_high       = 93,
+	},
+	{
+		/* 94 <= adc <= 167, stable zone */
+		.code           = KEY_PREVIOUSSONG,
+		.adc_low        = 94,
+		.adc_high       = 167,
+	},
+	{
+		/* 168 <= adc <= 370, stable zone */
+		.code           = KEY_NEXTSONG,
+		.adc_low        = 168,
+		.adc_high       = 370,
+	},
+};
+
+static int sec_jack_get_adc_value(void)
+{
+	return 0;
+}
+
+struct sec_jack_platform_data sec_jack_pdata = {
+	.set_micbias_state = sec_jack_set_micbias_state,
+	.get_adc_value = sec_jack_get_adc_value,
+	.zones = sec_jack_zones,
+	.num_zones = ARRAY_SIZE(sec_jack_zones),
+	.buttons_zones = sec_jack_buttons_zones,
+	.num_buttons_zones = ARRAY_SIZE(sec_jack_buttons_zones),
+	.det_gpio = GPIO_DET_35,
+};
+
+static struct platform_device sec_device_jack = {
+	.name                   = "sec_jack",
+	.id                     = 1, /* will be used also for gpio_event id */
+	.dev.platform_data      = &sec_jack_pdata,
+};
+
+void __init exynos5_manta_jack_init(void)
+{
+	s3c_gpio_cfgpin(GPIO_DET_35, S3C_GPIO_INPUT);
+	s3c_gpio_setpull(GPIO_DET_35, S3C_GPIO_PULL_NONE);
+
+	/* GPIO_DET_35 is inverted on <= PRE_ALPHA boards */
+	if (exynos5_manta_get_revision() <= MANTA_REV_PRE_ALPHA)
+		sec_jack_pdata.det_active_high = true;
+
+	platform_device_register(&sec_device_jack);
+}
diff --git a/arch/arm/mach-exynos/board-manta-media.c b/arch/arm/mach-exynos/board-manta-media.c
new file mode 100644
index 0000000..fad23c8
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta-media.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <media/exynos_gscaler.h>
+
+#include <plat/cpu.h>
+#include <plat/devs.h>
+#include <plat/fimg2d.h>
+#include <plat/jpeg.h>
+
+#include <mach/exynos-ion.h>
+#include <mach/exynos-mfc.h>
+#include <mach/sysmmu.h>
+
+static struct platform_device *media_devices[] __initdata = {
+	&s5p_device_mfc,
+	&exynos5_device_gsc0,
+	&exynos5_device_gsc1,
+	&exynos5_device_gsc2,
+	&exynos5_device_gsc3,
+	&s5p_device_jpeg,
+	&s5p_device_fimg2d,
+};
+
+static struct s5p_mfc_platdata manta_mfc_pd = {
+	.clock_rate = 333 * MHZ,
+};
+
+static struct fimg2d_platdata fimg2d_data __initdata = {
+	.hw_ver		= 0x42,
+	.gate_clkname	= "fimg2d",
+};
+
+static void __init manta_media_sysmmu_init(void)
+{
+	platform_set_sysmmu(&SYSMMU_PLATDEV(mfc_lr).dev, &s5p_device_mfc.dev);
+	platform_set_sysmmu(&SYSMMU_PLATDEV(gsc0).dev,
+			    &exynos5_device_gsc0.dev);
+	platform_set_sysmmu(&SYSMMU_PLATDEV(gsc1).dev,
+			    &exynos5_device_gsc1.dev);
+	platform_set_sysmmu(&SYSMMU_PLATDEV(gsc2).dev,
+			    &exynos5_device_gsc2.dev);
+	platform_set_sysmmu(&SYSMMU_PLATDEV(gsc3).dev,
+			    &exynos5_device_gsc3.dev);
+	platform_set_sysmmu(&SYSMMU_PLATDEV(jpeg).dev,
+			    &s5p_device_jpeg.dev);
+	platform_set_sysmmu(&SYSMMU_PLATDEV(2d).dev,
+			    &s5p_device_fimg2d.dev);
+}
+
+void __init exynos5_manta_media_init(void)
+{
+	manta_media_sysmmu_init();
+	s5p_mfc_set_platdata(&manta_mfc_pd);
+
+	s3c_set_platdata(&exynos_gsc0_default_data,
+			 sizeof(exynos_gsc0_default_data),
+			 &exynos5_device_gsc0);
+	s3c_set_platdata(&exynos_gsc1_default_data,
+			 sizeof(exynos_gsc1_default_data),
+			 &exynos5_device_gsc1);
+	s3c_set_platdata(&exynos_gsc2_default_data,
+			 sizeof(exynos_gsc2_default_data),
+			 &exynos5_device_gsc2);
+	s3c_set_platdata(&exynos_gsc3_default_data,
+			 sizeof(exynos_gsc3_default_data),
+			 &exynos5_device_gsc3);
+
+	s5p_fimg2d_set_platdata(&fimg2d_data);
+	exynos5_jpeg_setup_clock(&s5p_device_jpeg.dev, 150000000);
+
+	platform_add_devices(media_devices, ARRAY_SIZE(media_devices));
+}
+
diff --git a/arch/arm/mach-exynos/board-manta-nfc.c b/arch/arm/mach-exynos/board-manta-nfc.c
new file mode 100644
index 0000000..dcb7e5a
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta-nfc.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/i2c-gpio.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/spi/spi.h>
+#include <linux/nfc/bcm2079x.h>
+
+#include <plat/devs.h>
+#include <plat/gpio-cfg.h>
+#include <plat/s3c64xx-spi.h>
+
+#include <mach/spi-clocks.h>
+
+#define GPIO_NFC_WAKE		EXYNOS5_GPX1(5)
+#define GPIO_SPI_INT		EXYNOS5_GPX1(6)
+#define GPIO_NFC_EN		EXYNOS5_GPD1(7)
+#define GPIO_NFC_CS		EXYNOS5_GPB1(2)
+
+static void bcm2079x_nfc_setup_gpio(void)
+{
+	s3c_gpio_cfgpin(GPIO_SPI_INT, S3C_GPIO_SFN(S3C_GPIO_INPUT));
+	s3c_gpio_setpull(GPIO_SPI_INT, S3C_GPIO_PULL_NONE);
+
+	s3c_gpio_cfgpin(GPIO_NFC_EN, S3C_GPIO_SFN(S3C_GPIO_OUTPUT));
+	s3c_gpio_setpull(GPIO_NFC_EN, S3C_GPIO_PULL_UP);
+
+	s3c_gpio_cfgpin(GPIO_NFC_WAKE, S3C_GPIO_SFN(S3C_GPIO_OUTPUT));
+	s3c_gpio_setpull(GPIO_NFC_WAKE, S3C_GPIO_PULL_UP);
+}
+
+static struct bcm2079x_platform_data bcm2079x_spi_pdata = {
+	.irq_gpio = GPIO_SPI_INT,
+	.en_gpio = GPIO_NFC_EN,
+	.wake_gpio = GPIO_NFC_WAKE,
+};
+
+static struct s3c64xx_spi_csinfo spi2_csi[] = {
+	[0] = {
+		.line		= GPIO_NFC_CS,
+		.set_level	= gpio_set_value,
+		.fb_delay	= 0x0,
+	},
+};
+
+static struct spi_board_info spi2_board_info[] __initdata = {
+	{
+		.modalias		= "bcm2079x-spi",
+		.max_speed_hz		= 4 * MHZ,
+		.bus_num		= 2,
+		.chip_select		= 0,
+		.mode			= SPI_MODE_0,
+		.irq			= IRQ_EINT(14),
+		.controller_data	= &spi2_csi[0],
+		.platform_data		= &bcm2079x_spi_pdata,
+	}
+};
+
+void __init exynos5_manta_nfc_init(void)
+{
+	bcm2079x_nfc_setup_gpio();
+	exynos_spi_clock_setup(&s3c64xx_device_spi2.dev, 2);
+	if (!exynos_spi_cfg_cs(spi2_csi[0].line, 2)) {
+		s3c64xx_spi2_set_platdata(&s3c64xx_spi2_pdata,
+				EXYNOS_SPI_SRCCLK_SCLK, ARRAY_SIZE(spi2_csi));
+
+		spi_register_board_info(spi2_board_info,
+				ARRAY_SIZE(spi2_board_info));
+	} else {
+		pr_err("%s : Error requesting gpio for SPI-CH%d CS",
+			__func__, spi2_board_info->bus_num);
+	}
+
+	platform_device_register(&s3c64xx_device_spi2);
+}
diff --git a/arch/arm/mach-exynos/board-manta-pogo.c b/arch/arm/mach-exynos/board-manta-pogo.c
new file mode 100644
index 0000000..d0ad312
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta-pogo.c
@@ -0,0 +1,1103 @@
+/* arch/arm/mach-exynos/board-manta-pogo.c
+ *
+ * Copyright (C) 2012 Samsung Electronics.
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/ktime.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/cpufreq.h>
+
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/switch.h>
+#include <linux/wakelock.h>
+#include <linux/sched.h>
+
+#include <plat/gpio-cfg.h>
+#include "board-manta.h"
+#include <linux/platform_data/android_battery.h>
+
+/* including start and stop bits */
+#define CMD_READ_SZ		8
+#define CMD_WRITE_SZ		16
+#define RESP_READ_SZ		12
+#define RESP_WRITE_SZ		5
+
+#define RW_OFFSET		2
+#define ID_OFFSET		3
+#define STOP_R_OFFSET		6
+#define DATA_OFFSET		6
+#define STOP_W_OFFSET		14
+
+#define START_BITS		0x3
+#define STOP_BITS		0x1
+
+/* states for dock */
+#define POGO_UNDOCKED			0
+#define POGO_DESK_DOCK			1
+
+/* states for usb_audio */
+#define POGO_NO_AUDIO			0
+#define POGO_DIGITAL_AUDIO		2
+
+#define DOCK_STAT_POWER_MASK		3
+#define DOCK_STAT_POWER_OFFSET		2
+#define DOCK_STAT_POWER_NONE		0
+#define DOCK_STAT_POWER_500MA		1
+#define DOCK_STAT_POWER_2A		2
+#define DOCK_STAT_POWER_1A		3
+
+#define DOCK_STAT_AUDIO_MASK		3
+#define DOCK_STAT_AUDIO_OFFSET		0
+#define DOCK_STAT_AUDIO_CONNECTED	1
+#define DOCK_STAT_AUDIO_DISCONNECTED	0
+
+#define MFM_DELAY_NS_DEFAULT		(1000 * 10)
+#define RESPONSE_INTERVAL		5
+#define CMD_DELAY_USEC			100		/* 100us */
+#define TX_ERR_DELAY_USEC(delay_ns)	((delay_ns) / 1000 * 7 + CMD_DELAY_USEC)
+#define check_stop_bits(x, n) ((((x) >> ((n) - 2)) & 1) == (STOP_BITS & 1))
+#define raw_resp(x, n) ((x) & ((1 << ((n) - 2)) - 1));
+
+#define DEBOUNCE_DELAY_MS		250
+
+#define DOCK_CPU_FREQ_MIN		800000
+
+/* GPIO configuration */
+#define GPIO_POGO_DATA_BETA		EXYNOS5_GPB0(0)
+#define GPIO_POGO_DATA_POST_BETA	EXYNOS5_GPX0(5)
+#define GPIO_DOCK_PD_INT		EXYNOS5_GPX0(6)
+#define GPIO_POGO_SPDIF			EXYNOS5_GPB1(0)
+
+#define GPIO_POGO_SEL1			EXYNOS5_GPG1(0)
+#define GPIO_TA_CHECK_SEL		EXYNOS5_GPD1(5)
+#define GPIO_LDO20_EN			EXYNOS5_GPD1(4)
+
+static struct switch_dev dock_switch = {
+	.name = "dock",
+};
+
+static struct switch_dev usb_audio_switch = {
+	.name = "usb_audio",
+};
+
+enum pogo_debounce_state {
+	POGO_DEBOUNCE_UNDOCKED,		/* interrupt enabled,  timer stopped */
+	POGO_DEBOUNCE_UNSTABLE,		/* interrupt disabled, timer running */
+	POGO_DEBOUNCE_WAIT_STABLE,	/* interrupt enabled,  timer running */
+	POGO_DEBOUNCE_DOCKED,		/* interrupt enabled,  timer stopped */
+};
+
+struct dock_state {
+	struct mutex lock;
+	u32 t;
+	u32 last_edge_t[2];
+	u32 last_edge_i[2];
+	bool level;
+	bool dock_connected_unknown;
+	bool ignore_data_irq_once;
+
+	u32 mfm_delay_ns;
+	bool powered_dock_present;
+	struct workqueue_struct *dock_wq;
+	struct work_struct dock_work;
+	struct wake_lock wake_lock;
+
+	wait_queue_head_t wait;
+	int dock_pd_irq;
+	bool debounce_pending;
+	int spdif_mux_state;
+	enum pogo_debounce_state debounce_state;
+	struct timer_list debounce_timer;
+	struct work_struct debounce_work;
+	struct wake_lock debounce_wake_lock;
+
+	int ta_check_wait;
+	bool ta_check_mode;
+
+	unsigned int cpufreq_min;
+};
+
+static struct dock_state ds = {
+	.lock		= __MUTEX_INITIALIZER(ds.lock),
+	.mfm_delay_ns	= MFM_DELAY_NS_DEFAULT,
+	.wait		= __WAIT_QUEUE_HEAD_INITIALIZER(ds.wait),
+	.debounce_state	= POGO_DEBOUNCE_UNDOCKED,
+};
+
+static struct gpio manta_pogo_gpios[] = {
+	{GPIO_POGO_DATA_POST_BETA, GPIOF_IN, "dock"},
+	{GPIO_POGO_SEL1, GPIOF_OUT_INIT_HIGH, "pogo_sel1"},
+	{GPIO_TA_CHECK_SEL, GPIOF_OUT_INIT_LOW, "pogo_spdif_sel"},
+	{GPIO_DOCK_PD_INT, GPIOF_IN, "dock_pd_int"},
+	{GPIO_LDO20_EN, GPIOF_OUT_INIT_HIGH, "ldo20_en"},
+};
+
+#define _GPIO_DOCK		(manta_pogo_gpios[0].gpio)
+
+#define dock_out(n) gpio_direction_output(_GPIO_DOCK, n);
+#define dock_out2(n) gpio_set_value(_GPIO_DOCK, n)
+#define dock_in() gpio_direction_input(_GPIO_DOCK);
+#define dock_read() gpio_get_value(_GPIO_DOCK)
+#define dock_irq() s3c_gpio_cfgpin(_GPIO_DOCK, S3C_GPIO_SFN(0xF))
+
+static u32 pogo_read_fast_timer(void)
+{
+	return sched_clock();
+}
+
+static void pogo_set_min_cpu_freq(unsigned int khz)
+{
+	struct dock_state *s = &ds;
+	int i;
+
+	s->cpufreq_min = khz;
+
+	for_each_online_cpu(i)
+		cpufreq_update_policy(i);
+}
+
+static u16 make_cmd(int id, bool write, int data)
+{
+	u16 cmd = (id & 0x7) << ID_OFFSET | write << RW_OFFSET | START_BITS;
+	cmd |= STOP_BITS << (write ? STOP_W_OFFSET : STOP_R_OFFSET);
+	if (write)
+		cmd |= (data & 0xFF) << DATA_OFFSET;
+	return cmd;
+}
+
+static int dock_get_edge(struct dock_state *s, u32 timeout, u32 tmin, u32 tmax)
+{
+	bool lin;
+	bool in = s->level;
+	u32 t;
+	do {
+		lin = in;
+		in = dock_read();
+		t = pogo_read_fast_timer();
+		if (in != lin) {
+			s->last_edge_t[in] = t;
+			s->last_edge_i[in] = 0;
+			s->level = in;
+			if ((s32) (t - tmin) < 0 || (s32) (t - tmax) > 0)
+				return -1;
+			return 1;
+		}
+	} while ((s32) (t - timeout) < 0);
+	return 0;
+}
+
+static bool dock_sync(struct dock_state *s, u32 timeout)
+{
+	u32 t;
+
+	s->level = dock_read();
+	t = pogo_read_fast_timer();
+
+	if (!dock_get_edge(s, t + timeout, 0, 0))
+		return false;
+	s->last_edge_i[s->level] = 2;
+	return !!dock_get_edge(s, s->last_edge_t[s->level] + s->mfm_delay_ns *
+			4, 0, 0);
+}
+
+static int dock_get_next_bit(struct dock_state *s)
+{
+	u32 i = s->last_edge_i[!s->level] + ++s->last_edge_i[s->level];
+	u32 target = s->last_edge_t[!s->level] + s->mfm_delay_ns * i;
+	u32 timeout = target + s->mfm_delay_ns / 2;
+	u32 tmin = target - s->mfm_delay_ns / 4;
+	u32 tmax = target + s->mfm_delay_ns / 4;
+	return dock_get_edge(s, timeout, tmin, tmax);
+}
+
+static u32 dock_get_bits(struct dock_state *s, int count, int *errp)
+{
+	u32 data = 0;
+	u32 m = 1;
+	int ret;
+	int err = 0;
+	while (count--) {
+		ret = dock_get_next_bit(s);
+		if (ret)
+			data |= m;
+		if (ret < 0)
+			err++;
+		m <<= 1;
+	}
+	if (errp)
+		*errp = err;
+	return data;
+}
+
+static int dock_send_bits(struct dock_state *s, u32 data, int count, int period)
+{
+	u32 t, t0, to;
+
+	dock_out(s->level);
+	t = to = 0;
+	t0 = pogo_read_fast_timer();
+
+	while (count--) {
+		if (data & 1)
+			dock_out2((s->level = !s->level));
+
+		t = pogo_read_fast_timer() - t0;
+		if (t - to > period / 2) {
+			pr_debug("dock: to = %d, t = %d\n", to, t);
+			return -EIO;
+		}
+
+		data >>= 1;
+
+		to += period;
+		do {
+			t = pogo_read_fast_timer() - t0;
+		} while (t < to);
+		if (t - to > period / 4) {
+			pr_debug("dock: to = %d, t = %d\n", to, t);
+			return -EIO;
+		}
+	}
+	return 0;
+}
+
+static u32 mfm_encode(u16 data, int count, bool p)
+{
+	u32 mask;
+	u32 mfm = 0;
+	u32 clock = ~data & ~(data << 1 | !!p);
+	for (mask = 1UL << (count - 1); mask; mask >>= 1) {
+		mfm |= (data & mask);
+		mfm <<= 1;
+		mfm |= (clock & mask);
+	}
+	return mfm;
+}
+
+static u32 mfm_decode(u32 mfm)
+{
+	u32 data = 0;
+	u32 clock = 0;
+	u32 mask = 1;
+	while (mfm) {
+		if (mfm & 1)
+			clock |= mask;
+		mfm >>= 1;
+		if (mfm & 1)
+			data |= mask;
+		mfm >>= 1;
+		mask <<= 1;
+	}
+	return data;
+}
+
+static int dock_command(struct dock_state *s, u16 cmd, int len, int retlen)
+{
+	u32 mfm;
+	int count;
+	u32 data = cmd;
+	int tx, ret;
+	int err = -1;
+	unsigned long flags;
+
+	if (!s->cpufreq_min)
+		pogo_set_min_cpu_freq(DOCK_CPU_FREQ_MIN);
+
+	mfm = mfm_encode(data, len, false);
+	count = len * 2;
+
+	pr_debug("%s: data 0x%x mfm 0x%x\n", __func__, cmd, mfm);
+
+	local_irq_save(flags);
+	tx = dock_send_bits(s, mfm, count, s->mfm_delay_ns);
+
+	if (!tx) {
+		dock_in();
+		if (dock_sync(s, s->mfm_delay_ns * RESPONSE_INTERVAL)) {
+			ret = dock_get_bits(s, retlen * 2, &err);
+		} else {
+			pr_debug("%s: response sync error\n", __func__);
+			s->ignore_data_irq_once = s->level;
+			ret = -1;
+		}
+		dock_irq();
+	} else if (!s->level)
+		dock_irq();
+
+	local_irq_restore(flags);
+
+	udelay(tx < 0 ? TX_ERR_DELAY_USEC(s->mfm_delay_ns) : CMD_DELAY_USEC);
+
+	if (tx < 0) {
+		pr_debug("dock_command: %x: transmission err %d\n", cmd, tx);
+		return tx;
+	}
+	if (ret < 0) {
+		pr_debug("dock_command: %x: no response\n", cmd);
+		return ret;
+	}
+	data = mfm_decode(ret);
+	mfm = mfm_encode(data, retlen, true);
+	if (mfm != ret || err || !check_stop_bits(data, retlen)) {
+		pr_debug("dock_command: %x: bad response, data %x, mfm %x %x, err %d\n",
+						cmd, data, mfm, ret, err);
+		return -EIO;
+	}
+
+	return raw_resp(data, retlen);
+}
+
+static int dock_command_retry(struct dock_state *s, u16 cmd, size_t len,
+		size_t retlen)
+{
+	int retry = 20;
+	int ret;
+	while (retry--) {
+		ret = dock_command(s, cmd, len, retlen);
+		if (ret >= 0)
+			return ret;
+		if (retry != 19)
+			usleep_range(10000, 11000);
+	}
+	s->dock_connected_unknown = true;
+	if (s->level) {
+		/* put the line low so we can receive dock interrupts */
+		dock_out(s->level = false);
+		udelay(1);
+		dock_irq();
+	}
+	return -EIO;
+}
+
+static int dock_send_cmd(struct dock_state *s, int id, bool write, int data)
+{
+	int ret;
+	u16 cmd = make_cmd(id, write, data);
+
+	ret = dock_command_retry(s, cmd, write ? CMD_WRITE_SZ : CMD_READ_SZ,
+			write ? RESP_WRITE_SZ - 2 : RESP_READ_SZ - 2);
+
+	if (ret < 0)
+		pr_warning("%s: failed, cmd: %x, write: %d, data: %x\n",
+			__func__, cmd, write, data);
+
+	return ret;
+}
+
+static int dock_read_multi(struct dock_state *s, int addr, u8 *data, size_t len)
+{
+	int ret;
+	int i;
+	u8 suml, sumr = -1;
+
+	int retry = 20;
+	while (retry--) {
+		suml = 0;
+		for (i = 0; i <= len; i++) {
+			ret = dock_send_cmd(s, addr + i, false, 0);
+			if (ret < 0)
+				return ret;
+			if (i < len) {
+				data[i] = ret;
+				suml += ret;
+			} else
+				sumr = ret;
+		}
+		if (sumr == suml)
+			return 0;
+
+		pr_warning("dock_read_multi(%x): bad checksum, %x != %x\n",
+			   addr, sumr, suml);
+	}
+	return -EIO;
+}
+
+static int dock_write_multi(struct dock_state *s, int addr, u8 *data,
+		size_t len)
+{
+	int ret;
+	int i;
+	u8 sum;
+
+	int retry = 20;
+	while (retry--) {
+		sum = 0;
+		for (i = 0; i < len; i++) {
+			sum += data[i];
+			ret = dock_send_cmd(s, addr + i, true, data[i]);
+			if (ret < 0)
+				return ret;
+		}
+		ret = dock_send_cmd(s, addr + len, true, sum);
+		if (ret < 0)
+			return ret;
+		/* check sum error */
+		if (ret == 0)
+			continue;
+		return 0;
+	}
+	return -EIO;
+}
+
+static void manta_pogo_spdif_config(bool charger_detect_mode)
+{
+	if (charger_detect_mode) {
+		s3c_gpio_cfgpin(GPIO_POGO_SPDIF, S3C_GPIO_INPUT);
+		s3c_gpio_setpull(GPIO_POGO_SPDIF, S3C_GPIO_PULL_NONE);
+	} else {
+		s3c_gpio_cfgpin(GPIO_POGO_SPDIF, S3C_GPIO_SFN(0x4));
+		s3c_gpio_setpull(GPIO_POGO_SPDIF, S3C_GPIO_PULL_NONE);
+	}
+}
+
+static void pogo_dock_pd_interrupt_locked(int irq, void *data)
+{
+	struct dock_state *s = data;
+
+	switch (s->debounce_state) {
+	case POGO_DEBOUNCE_UNDOCKED:
+	case POGO_DEBOUNCE_DOCKED:
+		wake_lock(&s->debounce_wake_lock);
+		mod_timer(&s->debounce_timer,
+			jiffies + msecs_to_jiffies(DEBOUNCE_DELAY_MS));
+		s->debounce_state = POGO_DEBOUNCE_WAIT_STABLE;
+		break;
+	case POGO_DEBOUNCE_WAIT_STABLE:
+		/*
+		 * Disable IRQ line in case there is noise. It will be
+		 * re-enabled when the timer expires.
+		 */
+		s->debounce_state = POGO_DEBOUNCE_UNSTABLE;
+		disable_irq_nosync(s->dock_pd_irq);
+		break;
+	case POGO_DEBOUNCE_UNSTABLE:
+		break;
+	}
+}
+
+static void dock_spdif_switch_set(bool spdif_mode)
+{
+	struct dock_state *s = &ds;
+
+	pr_debug("%s: %d pending %d\n", __func__, spdif_mode,
+		s->debounce_pending);
+
+	s->spdif_mux_state = spdif_mode;
+	gpio_set_value(GPIO_TA_CHECK_SEL, spdif_mode);
+
+	if (!spdif_mode && s->debounce_pending) {
+		pogo_dock_pd_interrupt_locked(s->dock_pd_irq, s);
+		s->debounce_pending = false;
+	}
+}
+
+static void dock_set_audio_switch(bool audio_on)
+{
+	struct dock_state *s = &ds;
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->wait.lock, flags);
+	dock_spdif_switch_set(audio_on);
+	if (audio_on && s->debounce_state == POGO_DEBOUNCE_UNDOCKED)
+		pogo_dock_pd_interrupt_locked(s->dock_pd_irq, s);
+	spin_unlock_irqrestore(&s->wait.lock, flags);
+
+	switch_set_state(&usb_audio_switch, audio_on ?
+			POGO_DIGITAL_AUDIO : POGO_NO_AUDIO);
+}
+
+int manta_pogo_charge_detect_start(bool spdif_mode_and_gpio_in)
+{
+	struct dock_state *s = &ds;
+	unsigned long flags;
+	int ret = 0;
+
+	pr_debug("%s: %d\n", __func__, spdif_mode_and_gpio_in);
+
+	mutex_lock(&s->lock);
+
+	spin_lock_irqsave(&s->wait.lock, flags);
+
+	if (s->debounce_state == POGO_DEBOUNCE_UNSTABLE ||
+		s->debounce_state == POGO_DEBOUNCE_WAIT_STABLE) {
+
+		s->ta_check_wait = 10;
+		ret = wait_event_interruptible_locked(s->wait,
+			s->debounce_state == POGO_DEBOUNCE_DOCKED ||
+			s->debounce_state == POGO_DEBOUNCE_UNDOCKED ||
+			!s->ta_check_wait);
+
+		if (!s->ta_check_wait || ret) {
+			mutex_unlock(&s->lock);
+			ret = -EBUSY;
+			goto done;
+		}
+	}
+
+	s->ta_check_mode = true;
+	s->ta_check_wait = 0;
+
+	if (spdif_mode_and_gpio_in)
+		manta_pogo_spdif_config(true);
+	dock_spdif_switch_set(spdif_mode_and_gpio_in);
+	gpio_set_value(GPIO_POGO_SEL1, 0);
+done:
+	spin_unlock_irqrestore(&s->wait.lock, flags);
+
+	return ret;
+}
+
+void manta_pogo_charge_detect_end(void)
+{
+	struct dock_state *s = &ds;
+	unsigned long flags;
+
+	pr_debug("%s\n", __func__);
+
+	BUG_ON(!s->ta_check_mode);
+
+	spin_lock_irqsave(&s->wait.lock, flags);
+
+	s->ta_check_mode = false;
+
+	gpio_set_value(GPIO_POGO_SEL1, 1);
+	dock_spdif_switch_set(false);
+	manta_pogo_spdif_config(false);
+
+	spin_unlock_irqrestore(&s->wait.lock, flags);
+
+	mutex_unlock(&s->lock);
+}
+
+static int dock_acquire(struct dock_state *s, bool check_docked)
+{
+	int ret = 0;
+
+	mutex_lock(&s->lock);
+
+	if (check_docked && !s->powered_dock_present) {
+		mutex_unlock(&s->lock);
+		return -ENODEV;
+	}
+
+	pr_debug("%s: acquired dock\n", __func__);
+
+	s->level = dock_read();
+	if (s->level) {
+		udelay(s->mfm_delay_ns / 1000 * 2);
+		s->level = dock_read();
+	}
+
+	return ret;
+}
+
+static void dock_release(struct dock_state *s)
+{
+	if (s->cpufreq_min)
+		pogo_set_min_cpu_freq(0);
+
+	mutex_unlock(&s->lock);
+	pr_debug("%s: released dock\n", __func__);
+}
+
+enum {
+	DOCK_STATUS = 0x1,
+	DOCK_ID_ADDR = 0x2,
+	DOCK_VERSION = 0x7,
+};
+
+static int dock_check_status(struct dock_state *s,
+			     enum manta_charge_source *charge_source)
+{
+	int ret = 0;
+	int dock_stat, power;
+
+	if (s->dock_connected_unknown) {
+		/* force a new dock notification if a command failed */
+		dock_set_audio_switch(false);
+		s->dock_connected_unknown = false;
+	}
+
+	pr_debug("%s: sending status command\n", __func__);
+
+	dock_stat = dock_send_cmd(s, DOCK_STATUS, false, 0);
+
+	pr_debug("%s: Dock status %02x\n", __func__, dock_stat);
+	if (dock_stat >= 0) {
+		dock_set_audio_switch(dock_stat & DOCK_STAT_AUDIO_CONNECTED);
+
+		if (charge_source) {
+			s->powered_dock_present = true;
+			power = (dock_stat >> DOCK_STAT_POWER_OFFSET) &
+					DOCK_STAT_POWER_MASK;
+			switch (power) {
+			case DOCK_STAT_POWER_500MA:
+				*charge_source = MANTA_CHARGE_SOURCE_USB;
+				break;
+			case DOCK_STAT_POWER_1A:
+				*charge_source = MANTA_CHARGE_SOURCE_AC_OTHER;
+				break;
+			case DOCK_STAT_POWER_2A:
+				*charge_source = MANTA_CHARGE_SOURCE_AC_SAMSUNG;
+				break;
+			default:
+				pr_warn("%s: unknown dock power state %d, default to USB\n",
+							__func__, power);
+				*charge_source = MANTA_CHARGE_SOURCE_USB;
+			}
+		}
+
+		goto done;
+	}
+
+	dock_in();
+	ret = -ENOENT;
+	dock_set_audio_switch(false);
+done:
+	return ret;
+}
+
+int manta_pogo_set_vbus(bool status, enum manta_charge_source *charge_source)
+{
+	struct dock_state *s = &ds;
+	int ret = 0;
+
+	dock_acquire(s, false);
+
+	pr_debug("%s: status %d\n", __func__, status ? 1 : 0);
+
+	if (status) {
+		ret = dock_check_status(s, charge_source);
+	} else {
+		dock_in();
+		dock_set_audio_switch(false);
+		s->powered_dock_present = false;
+		s->dock_connected_unknown = false;
+
+		if (charge_source)
+			*charge_source = MANTA_CHARGE_SOURCE_NONE;
+	}
+
+	dock_release(s);
+	return ret;
+}
+
+static void dock_work_proc(struct work_struct *work)
+{
+	struct dock_state *s = container_of(work, struct dock_state,
+			dock_work);
+	wake_lock(&s->wake_lock);
+	manta_force_update_pogo_charger();
+	wake_unlock(&s->wake_lock);
+}
+
+static irqreturn_t pogo_data_interrupt(int irq, void *data)
+{
+	struct dock_state *s = data;
+	pr_debug("%s: irq %d\n", __func__, irq);
+
+	if (s->ignore_data_irq_once) {
+		pr_debug("%s: ignored unwanted data_irq\n", __func__);
+		s->ignore_data_irq_once = false;
+		goto done;
+	}
+
+	if (s->powered_dock_present) {
+		wake_lock(&s->wake_lock);
+		queue_work(s->dock_wq, &s->dock_work);
+	}
+done:
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pogo_dock_pd_interrupt(int irq, void *data)
+{
+	struct dock_state *s = data;
+	unsigned long flags;
+
+	pr_debug("%s: irq %d, state %d, gpio %d, mux %d, ta_mode %d\n",
+		__func__, irq, s->debounce_state,
+		gpio_get_value(GPIO_DOCK_PD_INT), s->spdif_mux_state,
+		s->ta_check_mode);
+
+	spin_lock_irqsave(&s->wait.lock, flags);
+
+	if (s->spdif_mux_state || s->ta_check_mode)
+		s->debounce_pending = true;
+	else
+		pogo_dock_pd_interrupt_locked(irq, data);
+
+	spin_unlock_irqrestore(&s->wait.lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static void pogo_debounce_timer(unsigned long data)
+{
+	struct dock_state *s = (struct dock_state *) data;
+	unsigned long flags;
+
+	pr_debug("%s: state %d, gpio %d\n", __func__, s->debounce_state,
+		gpio_get_value(GPIO_DOCK_PD_INT));
+
+	spin_lock_irqsave(&s->wait.lock, flags);
+
+	switch (s->debounce_state) {
+	case POGO_DEBOUNCE_UNSTABLE:
+		/*
+		 * The detect gpio changed in one the previous timeslots,
+		 * so enable the irq, reset the timer, and wait again. If the
+		 * detect gpio changed after we last disabled the interrupt we
+		 * will get anther interrupt right away and the state will go
+		 * back to POGO_DET_UNSTABLE.
+		 */
+		s->debounce_state = POGO_DEBOUNCE_WAIT_STABLE;
+		enable_irq(s->dock_pd_irq);
+		mod_timer(&s->debounce_timer,
+				jiffies + msecs_to_jiffies(DEBOUNCE_DELAY_MS));
+
+		if (s->ta_check_wait && !(--s->ta_check_wait))
+			wake_up_locked(&s->wait);
+
+		break;
+	case POGO_DEBOUNCE_WAIT_STABLE:
+		s->debounce_state = (s->spdif_mux_state ||
+			gpio_get_value(GPIO_DOCK_PD_INT)) ?
+			POGO_DEBOUNCE_DOCKED : POGO_DEBOUNCE_UNDOCKED;
+
+		if (s->ta_check_wait)
+			wake_up_locked(&s->wait);
+
+		queue_work(s->dock_wq, &s->debounce_work);
+		break;
+	default:
+		break;
+	}
+
+	spin_unlock_irqrestore(&s->wait.lock, flags);
+}
+
+static void debounce_work_proc(struct work_struct *work)
+{
+	struct dock_state *s = container_of(work, struct dock_state,
+			debounce_work);
+	unsigned long flags;
+	int state;
+
+	spin_lock_irqsave(&s->wait.lock, flags);
+	state = s->debounce_state;
+	spin_unlock_irqrestore(&s->wait.lock, flags);
+
+	switch (state) {
+	case POGO_DEBOUNCE_DOCKED:
+		switch_set_state(&dock_switch, POGO_DESK_DOCK);
+		break;
+	case POGO_DEBOUNCE_UNDOCKED:
+		switch_set_state(&dock_switch, POGO_UNDOCKED);
+		break;
+	default:
+		break;
+	}
+
+	spin_lock_irqsave(&s->wait.lock, flags);
+	if (state == s->debounce_state)
+		wake_unlock(&s->debounce_wake_lock);
+	spin_unlock_irqrestore(&s->wait.lock, flags);
+}
+
+#ifdef DEBUG
+static ssize_t dev_attr_vbus_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", ds.powered_dock_present ? 1 : 0);
+}
+
+static ssize_t dev_attr_powered_dock_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	if (size) {
+		manta_pogo_set_vbus(buf[0] == '1', NULL);
+		return size;
+	} else
+		return -EINVAL;
+}
+static DEVICE_ATTR(powered_dock, S_IRUGO | S_IWUSR,
+	dev_attr_powered_dock_show, dev_attr_powered_dock_store);
+#endif
+
+#ifdef DEBUG
+static ssize_t dev_attr_delay_ns_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", ds.mfm_delay_ns);
+}
+
+static ssize_t dev_attr_delay_ns_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	if (size) {
+		ds.mfm_delay_ns = simple_strtoul(buf, NULL, 10);
+		return size;
+	} else
+		return -EINVAL;
+}
+static DEVICE_ATTR(delay_ns, S_IRUGO | S_IWUSR, dev_attr_delay_ns_show,
+		dev_attr_delay_ns_store);
+#endif
+
+static ssize_t dev_attr_dock_id_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int ret;
+	u8 dock_id[4];
+
+	ret = dock_acquire(&ds, true);
+	if (ret < 0)
+		goto fail;
+	ret = dock_read_multi(&ds, DOCK_ID_ADDR, dock_id, 4);
+	dock_release(&ds);
+	if (ret < 0)
+		goto fail;
+
+	ret = sprintf(buf, "%02x:%02x:%02x:%02x\n\n",
+		dock_id[0], dock_id[1], dock_id[2], dock_id[3]);
+fail:
+	return ret;
+}
+
+static ssize_t dev_attr_dock_id_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	int ret, i;
+	int val[4];
+	u8 dock_id[4];
+
+	if (size < 11 || sscanf(buf, "%2x:%2x:%2x:%2x", &val[0], &val[1],
+		&val[2], &val[3]) != 4)
+		return -EINVAL;
+
+	for (i = 0; i < 4; i++)
+		dock_id[i] = val[i];
+
+	ret = dock_acquire(&ds, true);
+	if (ret < 0)
+		goto fail;
+	ret = dock_write_multi(&ds, DOCK_ID_ADDR, dock_id, 4);
+	dock_release(&ds);
+	if (ret < 0)
+		goto fail;
+
+	ret = size;
+fail:
+	return ret;
+}
+static DEVICE_ATTR(dock_id, S_IRUGO | S_IWUSR, dev_attr_dock_id_show,
+		dev_attr_dock_id_store);
+
+static ssize_t dev_attr_dock_ver_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = dock_acquire(&ds, true);
+	if (ret < 0)
+		goto fail;
+	ret = dock_send_cmd(&ds, DOCK_VERSION, false, 0);
+	dock_release(&ds);
+	if (ret < 0)
+		goto fail;
+
+	ret = sprintf(buf, "0x%02x\n", ret);
+fail:
+	return ret;
+}
+static DEVICE_ATTR(dock_ver, S_IRUGO, dev_attr_dock_ver_show, NULL);
+
+static ssize_t dev_attr_dock_status_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = dock_acquire(&ds, true);
+	if (ret < 0)
+		goto fail;
+	ret = dock_send_cmd(&ds, DOCK_STATUS, false, 0);
+	dock_release(&ds);
+	if (ret < 0)
+		goto fail;
+
+	ret = sprintf(buf, "0x%02x\n", ret);
+fail:
+	return ret;
+}
+static DEVICE_ATTR(dock_status, S_IRUGO, dev_attr_dock_status_show, NULL);
+
+static int pogo_cpufreq_notifier(struct notifier_block *nb,
+				 unsigned long event, void *data)
+{
+	struct dock_state *s = &ds;
+	struct cpufreq_policy *policy = data;
+
+	if (event != CPUFREQ_ADJUST)
+		goto done;
+
+	pr_debug("%s: adjusting cpu%d cpufreq to %d", __func__,
+			policy->cpu, s->cpufreq_min);
+
+	cpufreq_verify_within_limits(policy, s->cpufreq_min,
+			policy->cpuinfo.max_freq);
+
+done:
+	return 0;
+}
+
+static struct notifier_block pogo_cpufreq_notifier_block = {
+	.notifier_call = pogo_cpufreq_notifier,
+};
+
+void __init exynos5_manta_pogo_init(void)
+{
+	struct dock_state *s = &ds;
+	int ret;
+
+	if (exynos5_manta_get_revision() <= MANTA_REV_BETA)
+		manta_pogo_gpios[0].gpio = GPIO_POGO_DATA_BETA;
+
+	wake_lock_init(&s->wake_lock, WAKE_LOCK_SUSPEND, "dock");
+	wake_lock_init(&s->debounce_wake_lock, WAKE_LOCK_SUSPEND, "dock_pd");
+
+	INIT_WORK(&s->dock_work, dock_work_proc);
+	INIT_WORK(&s->debounce_work, debounce_work_proc);
+	s->dock_wq = create_singlethread_workqueue("dock");
+
+	setup_timer(&s->debounce_timer, pogo_debounce_timer, (unsigned long)s);
+
+	s3c_gpio_cfgpin(GPIO_POGO_SEL1, S3C_GPIO_OUTPUT);
+	s3c_gpio_setpull(GPIO_POGO_SEL1, S3C_GPIO_PULL_NONE);
+	s5p_gpio_set_pd_cfg(GPIO_POGO_SEL1, S5P_GPIO_PD_PREV_STATE);
+	s5p_gpio_set_pd_pull(GPIO_POGO_SEL1, S5P_GPIO_PD_UPDOWN_DISABLE);
+
+	s5p_gpio_set_pd_cfg(GPIO_TA_CHECK_SEL, S5P_GPIO_PD_PREV_STATE);
+	s5p_gpio_set_pd_pull(GPIO_TA_CHECK_SEL, S5P_GPIO_PD_UPDOWN_DISABLE);
+
+	ret = gpio_request_array(manta_pogo_gpios,
+				 ARRAY_SIZE(manta_pogo_gpios));
+	if (ret)
+		pr_err("%s: cannot request gpios\n", __func__);
+
+	ret = gpio_request(GPIO_POGO_SPDIF, "pogo_spdif");
+	if (ret)
+		pr_err("%s: cannot request gpio POGO_SPDIF\n", __func__);
+
+	manta_pogo_spdif_config(false);
+
+	if (switch_dev_register(&dock_switch) == 0) {
+		ret = device_create_file(dock_switch.dev, &dev_attr_dock_id);
+		WARN_ON(ret);
+		ret = device_create_file(dock_switch.dev, &dev_attr_dock_ver);
+		WARN_ON(ret);
+		ret = device_create_file(dock_switch.dev,
+					 &dev_attr_dock_status);
+		WARN_ON(ret);
+#ifdef DEBUG
+		ret = device_create_file(dock_switch.dev, &dev_attr_delay_ns);
+		WARN_ON(ret);
+		ret = device_create_file(dock_switch.dev,
+			&dev_attr_unpowered_dock);
+		WARN_ON(ret);
+#endif
+	}
+
+	WARN_ON(switch_dev_register(&usb_audio_switch));
+
+	ret = cpufreq_register_notifier(&pogo_cpufreq_notifier_block,
+				  CPUFREQ_POLICY_NOTIFIER);
+	if (ret)
+		pr_warn("%s: cannot register cpufreq notifier\n", __func__);
+
+	manta_pogo_set_vbus(0, NULL);
+}
+
+int __init pogo_data_irq_subsys_init(void)
+{
+	struct dock_state *s = &ds;
+	int ret, data_irq;
+
+	dock_in();
+	s3c_gpio_setpull(_GPIO_DOCK, S3C_GPIO_PULL_NONE);
+
+	data_irq = gpio_to_irq(_GPIO_DOCK);
+
+	ret = request_irq(data_irq, pogo_data_interrupt, IRQF_TRIGGER_FALLING,
+			"dock", &ds);
+	if (ret < 0) {
+		pr_err("%s: failed to request irq %d, rc: %d\n", __func__,
+			data_irq, ret);
+		goto done;
+	}
+
+	ret = enable_irq_wake(data_irq);
+	if (ret) {
+		pr_err("%s: failed to enable irq_wake for POGO_DATA\n",
+			__func__);
+		goto fail_data_irq_wake;
+	}
+
+	s3c_gpio_cfgpin(GPIO_DOCK_PD_INT, S3C_GPIO_SFN(0xF));
+	s3c_gpio_setpull(GPIO_DOCK_PD_INT, S3C_GPIO_PULL_NONE);
+
+	s->dock_pd_irq = gpio_to_irq(GPIO_DOCK_PD_INT);
+
+	ret = request_irq(s->dock_pd_irq, pogo_dock_pd_interrupt,
+		IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "dock_pd", &ds);
+
+	if (ret < 0) {
+		pr_err("%s: failed to request dock_pd_int irq %d, rc: %d\n",
+			__func__, s->dock_pd_irq, ret);
+		goto fail_pd_irq_request;
+	}
+
+	ret = enable_irq_wake(s->dock_pd_irq);
+	if (ret < 0) {
+		pr_err("%s: failed to enable irq_wake for DOCK_PD_INT\n",
+			__func__);
+		goto fail_pd_irq_wake;
+	}
+
+	return 0;
+
+fail_pd_irq_wake:
+	free_irq(s->dock_pd_irq, &ds);
+
+fail_pd_irq_request:
+	disable_irq_wake(data_irq);
+
+fail_data_irq_wake:
+	free_irq(data_irq, &ds);
+
+done:
+	return ret;
+}
+
+subsys_initcall_sync(pogo_data_irq_subsys_init);
diff --git a/arch/arm/mach-exynos/board-manta-power.c b/arch/arm/mach-exynos/board-manta-power.c
new file mode 100644
index 0000000..45a2a6d
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta-power.c
@@ -0,0 +1,459 @@
+/* linux/arch/arm/mach-exynos/board-manta-power.c
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.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/gpio.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/mfd/max77686.h>
+#include <linux/regulator/machine.h>
+#include <linux/rtc.h>
+#include <linux/regulator/fixed.h>
+#include <linux/platform_device.h>
+
+#include <asm/system_misc.h>
+
+#include <mach/regs-pmu.h>
+#include <mach/asv-exynos.h>
+
+#include "board-manta.h"
+#include "common.h"
+
+#define REBOOT_MODE_PREFIX	0x12345670
+#define REBOOT_MODE_NONE	0
+#define REBOOT_MODE_RECOVERY	4
+#define REBOOT_MODE_FAST_BOOT	7
+
+#define REBOOT_MODE_NO_LPM	0x12345678
+#define REBOOT_MODE_LPM		0
+
+static struct regulator_consumer_supply ldo3_supply[] = {
+	REGULATOR_SUPPLY("vcc_1.8v", NULL),
+	REGULATOR_SUPPLY("AVDD2", NULL),
+	REGULATOR_SUPPLY("CPVDD", NULL),
+	REGULATOR_SUPPLY("DBVDD1", NULL),
+	REGULATOR_SUPPLY("DBVDD2", NULL),
+	REGULATOR_SUPPLY("DBVDD3", NULL)
+};
+
+static struct regulator_consumer_supply ldo8_supply[] = {
+	REGULATOR_SUPPLY("vmipi_1.0v", NULL),
+};
+
+static struct regulator_consumer_supply ldo9_supply[] = {
+	REGULATOR_SUPPLY("vdd", "3-004a"),
+};
+
+static struct regulator_consumer_supply ldo10_supply[] = {
+	REGULATOR_SUPPLY("vmipi_1.8v", NULL),
+};
+
+static struct regulator_consumer_supply ldo12_supply[] = {
+	REGULATOR_SUPPLY("votg_3.0v", NULL),
+};
+
+static struct regulator_consumer_supply ldo15_supply[] = {
+	REGULATOR_SUPPLY("vhsic_1.0v", NULL),
+};
+
+static struct regulator_consumer_supply ldo16_supply[] = {
+	REGULATOR_SUPPLY("vhsic_1.8v", NULL),
+};
+
+static struct regulator_consumer_supply ldo17_supply[] = {
+	REGULATOR_SUPPLY("5m_core_1.5v", NULL),
+};
+
+static struct regulator_consumer_supply ldo18_supply[] = {
+	REGULATOR_SUPPLY("cam_io_1.8v", NULL),
+};
+
+static struct regulator_consumer_supply ldo19_supply[] = {
+	REGULATOR_SUPPLY("vt_cam_1.8v", NULL),
+};
+
+static struct regulator_consumer_supply ldo20_supply[] = {
+	REGULATOR_SUPPLY("ta_check_1.35v", NULL),
+};
+
+static struct regulator_consumer_supply ldo23_supply[] = {
+	REGULATOR_SUPPLY("avdd", "3-004a"),
+};
+
+static struct regulator_consumer_supply ldo24_supply[] = {
+	REGULATOR_SUPPLY("cam_af_2.8v", NULL),
+};
+
+static struct regulator_consumer_supply ldo25_supply[] = {
+	REGULATOR_SUPPLY("vadc_3.3v", NULL),
+};
+
+static struct regulator_consumer_supply max77686_buck1 =
+REGULATOR_SUPPLY("vdd_mif", NULL);
+
+static struct regulator_consumer_supply max77686_buck2 =
+REGULATOR_SUPPLY("vdd_arm", NULL);
+
+static struct regulator_consumer_supply max77686_buck3 =
+REGULATOR_SUPPLY("vdd_int", NULL);
+
+static struct regulator_consumer_supply max77686_buck4 =
+REGULATOR_SUPPLY("vdd_g3d", NULL);
+
+static struct regulator_consumer_supply max77686_enp32khz[] = {
+	REGULATOR_SUPPLY("lpo_in", "bcm47511"),
+	REGULATOR_SUPPLY("lpo", "bcm4334_bluetooth"),
+};
+
+#define REGULATOR_INIT(_ldo, _name, _min_uV, _max_uV, _always_on, _ops_mask, \
+		       _disabled)					\
+	static struct regulator_init_data _ldo##_init_data = {		\
+		.constraints = {					\
+			.name	= _name,				\
+			.min_uV = _min_uV,				\
+			.max_uV = _max_uV,				\
+			.always_on	= _always_on,			\
+			.boot_on	= _always_on,			\
+			.apply_uV	= 1,				\
+			.valid_ops_mask = _ops_mask,			\
+			.state_mem	= {				\
+				.disabled =				\
+					(_disabled == -1 ? 0 : _disabled),\
+				.enabled =				\
+					(_disabled == -1 ? 0 : !(_disabled)),\
+			},						\
+		},							\
+		.num_consumer_supplies = ARRAY_SIZE(_ldo##_supply),	\
+		.consumer_supplies = &_ldo##_supply[0],			\
+	};
+
+REGULATOR_INIT(ldo3, "VCC_1.8V_AP", 1800000, 1800000, 1, 0, 0);
+REGULATOR_INIT(ldo8, "VMIPI_1.0V", 1000000, 1000000, 1,
+	       REGULATOR_CHANGE_STATUS, 1);
+REGULATOR_INIT(ldo9, "TOUCH_VDD_1.8V", 1800000, 1800000, 0,
+	       REGULATOR_CHANGE_STATUS, 1);
+REGULATOR_INIT(ldo10, "VMIPI_1.8V", 1800000, 1800000, 1,
+	       REGULATOR_CHANGE_STATUS, 1);
+REGULATOR_INIT(ldo12, "VUOTG_3.0V", 3000000, 3000000, 1,
+	       REGULATOR_CHANGE_STATUS, 1);
+REGULATOR_INIT(ldo15, "VHSIC_1.0V", 1000000, 1000000, 1,
+	       REGULATOR_CHANGE_STATUS, 1);
+REGULATOR_INIT(ldo16, "VHSIC_1.8V", 1800000, 1800000, 1,
+	       REGULATOR_CHANGE_STATUS, 1);
+REGULATOR_INIT(ldo17, "5M_CORE_1.5V", 1500000, 1500000, 0,
+	       REGULATOR_CHANGE_STATUS, 1);
+REGULATOR_INIT(ldo18, "CAM_IO_1.8V", 1800000, 1800000, 0,
+	       REGULATOR_CHANGE_STATUS, 1);
+REGULATOR_INIT(ldo19, "VT_CAM_1.8V", 1800000, 1800000, 0,
+	       REGULATOR_CHANGE_STATUS, 1);
+REGULATOR_INIT(ldo20, "TA_CHECK_1.35V", 1350000, 1350000, 0,
+	       REGULATOR_CHANGE_STATUS, 1);
+REGULATOR_INIT(ldo23, "TSP_AVDD_2.8V", 2800000, 2800000, 0,
+	       REGULATOR_CHANGE_STATUS, 1);
+REGULATOR_INIT(ldo24, "CAM_AF_2.8V", 2800000, 2800000, 0,
+	       REGULATOR_CHANGE_STATUS, 1);
+REGULATOR_INIT(ldo25, "VADC_3.3V", 3300000, 3300000, 1,
+	       REGULATOR_CHANGE_STATUS, 1);
+
+static struct regulator_init_data max77686_buck1_data = {
+	.constraints = {
+			.name = "vdd_mif range",
+			.min_uV = 850000,
+			.max_uV = 1200000,
+			.always_on = 1,
+			.boot_on = 1,
+			.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+			},
+	.num_consumer_supplies = 1,
+	.consumer_supplies = &max77686_buck1,
+};
+
+static struct regulator_init_data max77686_buck2_data = {
+	.constraints = {
+			.name = "vdd_arm range",
+			.min_uV = 850000,
+			.max_uV = 1500000,
+			.always_on = 1,
+			.boot_on = 1,
+			.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+			},
+	.num_consumer_supplies = 1,
+	.consumer_supplies = &max77686_buck2,
+};
+
+static struct regulator_init_data max77686_buck3_data = {
+	.constraints = {
+			.name = "vdd_int range",
+			.min_uV = 850000,
+			.max_uV = 1300000,
+			.always_on = 1,
+			.boot_on = 1,
+			.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+			},
+	.num_consumer_supplies = 1,
+	.consumer_supplies = &max77686_buck3,
+};
+
+static struct regulator_init_data max77686_buck4_data = {
+	.constraints = {
+			.name = "vdd_g3d range",
+			.min_uV = 850000,
+			.max_uV = 1250000,
+			.boot_on = 1,
+			.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+			REGULATOR_CHANGE_STATUS,
+			},
+	.num_consumer_supplies = 1,
+	.consumer_supplies = &max77686_buck4,
+};
+
+static struct regulator_init_data max77686_enp32khz_data = {
+	.constraints = {
+			.name = "32KHZ_PMIC",
+			.always_on = 1,
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+			.state_mem = {
+				      .enabled = 1,
+				      .disabled = 0,
+				      },
+			},
+	.num_consumer_supplies = ARRAY_SIZE(max77686_enp32khz),
+	.consumer_supplies = max77686_enp32khz,
+};
+
+static struct max77686_regulator_data max77686_regulators[] = {
+	{MAX77686_BUCK1, &max77686_buck1_data,},
+	{MAX77686_BUCK2, &max77686_buck2_data,},
+	{MAX77686_BUCK3, &max77686_buck3_data,},
+	{MAX77686_BUCK4, &max77686_buck4_data,},
+	{MAX77686_LDO3, &ldo3_init_data,},
+	{MAX77686_LDO8, &ldo8_init_data,},
+	{MAX77686_LDO9, &ldo9_init_data,},
+	{MAX77686_LDO10, &ldo10_init_data,},
+	{MAX77686_LDO12, &ldo12_init_data,},
+	{MAX77686_LDO15, &ldo15_init_data,},
+	{MAX77686_LDO16, &ldo16_init_data,},
+	{MAX77686_LDO17, &ldo17_init_data,},
+	{MAX77686_LDO18, &ldo18_init_data,},
+	{MAX77686_LDO19, &ldo19_init_data,},
+	{MAX77686_LDO20, &ldo20_init_data,},
+	{MAX77686_LDO23, &ldo23_init_data,},
+	{MAX77686_LDO24, &ldo24_init_data,},
+	{MAX77686_LDO25, &ldo25_init_data,},
+	{MAX77686_P32KH, &max77686_enp32khz_data,},
+};
+
+struct max77686_opmode_data max77686_opmode_data[MAX77686_REG_MAX] = {
+	[MAX77686_LDO3] = {MAX77686_LDO3, MAX77686_OPMODE_NORMAL},
+	[MAX77686_LDO8] = {MAX77686_LDO8, MAX77686_OPMODE_STANDBY},
+	[MAX77686_LDO10] = {MAX77686_LDO10, MAX77686_OPMODE_STANDBY},
+	[MAX77686_LDO12] = {MAX77686_LDO12, MAX77686_OPMODE_STANDBY},
+	[MAX77686_LDO15] = {MAX77686_LDO15, MAX77686_OPMODE_STANDBY},
+	[MAX77686_LDO16] = {MAX77686_LDO16, MAX77686_OPMODE_STANDBY},
+	[MAX77686_BUCK1] = {MAX77686_BUCK1, MAX77686_OPMODE_STANDBY},
+	[MAX77686_BUCK2] = {MAX77686_BUCK2, MAX77686_OPMODE_STANDBY},
+	[MAX77686_BUCK3] = {MAX77686_BUCK3, MAX77686_OPMODE_STANDBY},
+	[MAX77686_BUCK4] = {MAX77686_BUCK4, MAX77686_OPMODE_STANDBY},
+};
+
+static struct max77686_wtsr_smpl wtsr_smpl_data = {
+	.wtsr_en = true,
+	.smpl_en = true,
+	.wtsr_timer_val = 3,	/* 1000ms */
+	.smpl_timer_val = 0,	/* 0.5s */
+	.check_jigon = true,
+};
+
+/* If it's first boot, reset rtc to 1/1/2012 12:00:00(SUN) */
+static struct rtc_time init_time_data = {
+	.tm_sec = 0,
+	.tm_min = 0,
+	.tm_hour = 12,
+	.tm_wday = 0,
+	.tm_mday = 1,
+	.tm_mon = 0,
+	.tm_year = 112,
+	.tm_yday = 0,
+	.tm_isdst = 0,
+};
+
+static struct max77686_platform_data manta_max77686_info = {
+	.num_regulators = ARRAY_SIZE(max77686_regulators),
+	.regulators = max77686_regulators,
+	.irq_gpio = EXYNOS5_GPX0(2),	/* AP_PMIC_IRQ */
+	.irq_base = MANTA_IRQ_BOARD_PMIC_START,
+	.wakeup = 1,
+
+	.opmode_data = max77686_opmode_data,
+	.ramp_rate = MAX77686_RAMP_RATE_27MV,
+	.has_full_constraints = 1,
+
+	.buck234_gpio_dvs = {
+			     EXYNOS5_GPV0(7),	/* GPIO_PMIC_DVS1, */
+			     EXYNOS5_GPV0(6),	/* GPIO_PMIC_DVS2, */
+			     EXYNOS5_GPV0(5),	/* GPIO_PMIC_DVS3, */
+			     },
+	.buck234_gpio_selb = {
+			      EXYNOS5_GPV0(4),	/* GPIO_BUCK2_SEL, */
+			      EXYNOS5_GPV0(1),	/* GPIO_BUCK3_SEL, */
+			      EXYNOS5_GPV0(0),	/* GPIO_BUCK4_SEL, */
+			      },
+
+	.buck2_voltage[0] = 1075000,	/* 1.075V */
+	.buck2_voltage[1] = 1075000,	/* 1.075V */
+	.buck2_voltage[2] = 1075000,	/* 1.075V */
+	.buck2_voltage[3] = 1075000,	/* 1.075V */
+	.buck2_voltage[4] = 1075000,	/* 1.075V */
+	.buck2_voltage[5] = 1075000,	/* 1.075V */
+	.buck2_voltage[6] = 1075000,	/* 1.075V */
+	.buck2_voltage[7] = 1075000,	/* 1.075V */
+
+	.buck3_voltage[0] = 1037500,	/* 1.0375V */
+	.buck3_voltage[1] = 1037500,	/* 1.0375V */
+	.buck3_voltage[2] = 1037500,	/* 1.0375V */
+	.buck3_voltage[3] = 1037500,	/* 1.0375V */
+	.buck3_voltage[4] = 1037500,	/* 1.0375V */
+	.buck3_voltage[5] = 1037500,	/* 1.0375V */
+	.buck3_voltage[6] = 1037500,	/* 1.0375V */
+	.buck3_voltage[7] = 1037500,	/* 1.0375V */
+
+	.buck4_voltage[0] = 1200000,	/* 1.2V */
+	.buck4_voltage[1] = 1200000,	/* 1.2V */
+	.buck4_voltage[2] = 1200000,	/* 1.2V */
+	.buck4_voltage[3] = 1200000,	/* 1.2V */
+	.buck4_voltage[4] = 1200000,	/* 1.2V */
+	.buck4_voltage[5] = 1200000,	/* 1.2V */
+	.buck4_voltage[6] = 1200000,	/* 1.2V */
+	.buck4_voltage[7] = 1200000,	/* 1.2V */
+
+	/* for RTC */
+	.wtsr_smpl = &wtsr_smpl_data,
+	.init_time = &init_time_data,
+};
+
+static struct i2c_board_info i2c_devs5[] __initdata = {
+	{
+		I2C_BOARD_INFO("max77686", (0x12 >> 1)),
+		.platform_data = &manta_max77686_info,
+	},
+};
+
+/* Fixed regulators */
+static struct regulator_consumer_supply mxt_xvdd_supply =
+	REGULATOR_SUPPLY("xvdd", "3-004a");
+
+static struct regulator_init_data mxt_booster_voltage_init_data = {
+	.constraints = {
+		.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+	},
+};
+
+static struct fixed_voltage_config mxt_booster_regulator_data = {
+	.supply_name = "MXT_BOOSTER",
+	.gpio = EXYNOS5_GPD1(1),
+	.enable_high = 1,
+	.init_data = &mxt_booster_voltage_init_data,
+};
+
+static struct platform_device mxt_booster_regulator_device = {
+	.name = "reg-fixed-voltage",
+	.id = 0,
+	.dev = {
+		.platform_data = &mxt_booster_regulator_data,
+	}
+};
+
+static struct regulator_init_data mxt_xvdd_voltage_init_data = {
+	.constraints = {
+		.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+	},
+	.num_consumer_supplies	= 1,
+	.consumer_supplies	= &mxt_xvdd_supply,
+	.supply_regulator = "MXT_BOOSTER",
+};
+
+static struct fixed_voltage_config mxt_xvdd_regulator_data = {
+	.supply_name = "MXT_XVDD",
+	.gpio = EXYNOS5_GPG0(1),
+	.startup_delay = 3000,
+	.enable_high = 1,
+	.init_data = &mxt_xvdd_voltage_init_data,
+};
+
+static struct platform_device mxt_xvdd_regulator_device = {
+	.name = "reg-fixed-voltage",
+	.id = 1,
+	.dev = {
+		.platform_data = &mxt_xvdd_regulator_data,
+	}
+};
+
+static struct platform_device *mxt_fixed_regulator_devices[] __initdata = {
+	&mxt_booster_regulator_device,
+	&mxt_xvdd_regulator_device,
+};
+
+/* Always reboot to the bootloader. Let the bootloader
+ * figure out if we should truly power-off or charging.
+ */
+static void manta_power_off(void)
+{
+	local_irq_disable();
+
+	writel(REBOOT_MODE_LPM, EXYNOS_INFORM2); /* Enter lpm mode */
+	writel(REBOOT_MODE_PREFIX | REBOOT_MODE_NONE, EXYNOS_INFORM3);
+
+	exynos5_restart(0, 0);
+}
+
+static void manta_reboot(char str, const char *cmd)
+{
+	local_irq_disable();
+
+	writel(REBOOT_MODE_NO_LPM, EXYNOS_INFORM2); /* Don't enter lpm mode */
+	writel(REBOOT_MODE_PREFIX | REBOOT_MODE_NONE, EXYNOS_INFORM3);
+
+	if (cmd) {
+		if (!strcmp(cmd, "recovery"))
+			writel(REBOOT_MODE_PREFIX | REBOOT_MODE_RECOVERY,
+			       EXYNOS_INFORM3);
+		else if (!strcmp(cmd, "bootloader"))
+			writel(REBOOT_MODE_PREFIX | REBOOT_MODE_FAST_BOOT,
+			       EXYNOS_INFORM2);
+	}
+
+	exynos5_restart(str, cmd); /* S/W reset: INFORM0~3:  Keep its value */
+}
+
+extern unsigned int
+exynos5250_set_volt(enum asv_type_id target_type, unsigned int target_freq,
+			unsigned int group, unsigned int volt);
+
+void __init exynos5_manta_adjust_mif_asv_table(void)
+{
+	int i;
+	unsigned int adj_mif_volt[] = { 1175000, 1125000, 1100000, 1037500 };
+
+	if (exynos5_manta_get_revision() > MANTA_REV_DOGFOOD02)
+		return;
+
+	for (i = 0; i < ARRAY_SIZE(adj_mif_volt); i++)
+		exynos5250_set_volt(ID_MIF, 800000, i, adj_mif_volt[i]);
+}
+
+void __init exynos5_manta_power_init(void)
+{
+	pm_power_off = manta_power_off;
+	arm_pm_restart = manta_reboot;
+
+	i2c_register_board_info(5, i2c_devs5, ARRAY_SIZE(i2c_devs5));
+
+	platform_add_devices(mxt_fixed_regulator_devices,
+				ARRAY_SIZE(mxt_fixed_regulator_devices));
+}
diff --git a/arch/arm/mach-exynos/board-manta-sensors.c b/arch/arm/mach-exynos/board-manta-sensors.c
new file mode 100644
index 0000000..e573af3
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta-sensors.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/platform_data/bh1721fvc.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/mpu.h>
+#include <plat/gpio-cfg.h>
+
+#define GPIO_ACC_INT	EXYNOS5_GPX1(4)
+#define EINT_ACC	12
+#define GPIO_MSENSE_RST	EXYNOS5_GPG2(0)
+
+static struct mpu_platform_data mpu_data = {
+	.int_config  = 0x00,
+	.level_shifter = 0,
+	.orientation = {
+		0,  1,  0,
+		1,  0,  0,
+		0,  0, -1,
+	},
+	.sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+	.sec_slave_id   = COMPASS_ID_AK8963,
+	.secondary_i2c_addr = 0x0C,
+	.secondary_orientation = {
+		-1,  0,  0,
+		 0,  1,  0,
+		 0,  0, -1,
+	},
+	.key = {
+		221,  22, 205,   7, 217, 186, 151, 55,
+		206, 254,  35, 144, 225, 102,  47, 50,
+	},
+};
+
+static struct i2c_board_info __initdata manta_sensors_i2c1_boardinfo[] = {
+	{
+		I2C_BOARD_INFO("bmp182", 0x77),
+	},
+	{
+		I2C_BOARD_INFO("mpu6050", 0x68),
+		.irq = IRQ_EINT(EINT_ACC),
+		.platform_data = &mpu_data,
+	},
+};
+
+#define GPIO_ALS_NRST	EXYNOS5_GPH1(2)
+
+static struct bh1721fvc_platform_data bh1721fvc_pdata = {
+	.reset_pin = GPIO_ALS_NRST,
+};
+
+static struct i2c_board_info __initdata manta_sensors_i2c2_boardinfo[] = {
+	{
+		I2C_BOARD_INFO("bh1721fvc", 0x23),
+		.platform_data = &bh1721fvc_pdata,
+	},
+};
+
+void __init exynos5_manta_sensors_init(void)
+{
+	s3c_gpio_setpull(GPIO_ACC_INT, S3C_GPIO_PULL_UP);
+
+	gpio_request_one(GPIO_ACC_INT, GPIOF_IN, "ACC_INT");
+	gpio_request_one(GPIO_MSENSE_RST, GPIOF_OUT_INIT_HIGH, "MSENSE_RST");
+
+	i2c_register_board_info(1, manta_sensors_i2c1_boardinfo,
+		ARRAY_SIZE(manta_sensors_i2c1_boardinfo));
+
+	s3c_gpio_setpull(GPIO_ALS_NRST, S3C_GPIO_PULL_NONE);
+	i2c_register_board_info(2, manta_sensors_i2c2_boardinfo,
+		ARRAY_SIZE(manta_sensors_i2c2_boardinfo));
+}
diff --git a/arch/arm/mach-exynos/board-manta-vibrator.c b/arch/arm/mach-exynos/board-manta-vibrator.c
new file mode 100644
index 0000000..4f3aabf
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta-vibrator.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Co. Ltd. All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+#include <linux/platform_data/haptic_isa1200.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <plat/gpio-cfg.h>
+#include <plat/devs.h>
+
+#define VIB_PWM_GPIO EXYNOS5_GPB2(1)
+
+static struct isa1200_platform_data isa1200_pdata = {
+	.pwm_ch		= 1, /* XPWMTOUT_1 */
+	.hap_en_gpio	= EXYNOS5_GPD1(6),
+	.max_timeout	= 10000, /* 10 seconds */
+};
+
+/* I2C4 */
+static struct i2c_board_info i2c_devs4[] __initdata = {
+	{
+		I2C_BOARD_INFO("isa1200", (0x90 >> 1)),
+		.platform_data  = &isa1200_pdata,
+	},
+};
+
+void __init exynos5_manta_vib_init(void)
+{
+	int ret;
+	ret = s3c_gpio_cfgpin(VIB_PWM_GPIO, S3C_GPIO_SFN(2));
+	if (ret) {
+		printk(KERN_ERR "%s s3c_gpio_cfgpin error %d\n",
+				__func__, ret);
+		goto init_fail;
+	}
+	ret = i2c_register_board_info(4, i2c_devs4, ARRAY_SIZE(i2c_devs4));
+	if (ret) {
+		printk(KERN_ERR "%s i2c_register_board_info error %d\n",
+				__func__, ret);
+		goto init_fail;
+	}
+	ret = platform_device_register(&s3c_device_timer[isa1200_pdata.pwm_ch]);
+	if (ret)
+		printk(KERN_ERR "%s failed platform_device_register with error %d\n",
+				__func__, ret);
+
+init_fail:
+	return;
+}
diff --git a/arch/arm/mach-exynos/board-manta-wifi.c b/arch/arm/mach-exynos/board-manta-wifi.c
new file mode 100644
index 0000000..42b0735
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta-wifi.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <asm/mach-types.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/setup.h>
+#include <linux/if.h>
+#include <linux/skbuff.h>
+#include <linux/wlan_plat.h>
+#include <linux/mmc/host.h>
+#include <plat/gpio-cfg.h>
+#include <plat/devs.h>
+#include <mach/dwmci.h>
+#include <mach/map.h>
+
+#include <linux/random.h>
+#include <linux/jiffies.h>
+
+#define GPIO_WLAN_PMENA		EXYNOS5_GPV1(0)
+#define GPIO_WLAN_IRQ		EXYNOS5_GPX2(5)
+
+#define WLAN_SDIO_CMD		EXYNOS5_GPC2(1)
+#define WLAN_SDIO_DATA0	EXYNOS5_GPC2(3)
+#define WLAN_SDIO_DATA1	EXYNOS5_GPC2(4)
+#define WLAN_SDIO_DATA2	EXYNOS5_GPC2(5)
+#define WLAN_SDIO_DATA3	EXYNOS5_GPC2(6)
+
+extern void bt_wlan_lock(void);
+extern void bt_wlan_unlock(void);
+
+static struct resource manta_wifi_resources[] = {
+	[0] = {
+		.name	= "bcmdhd_wlan_irq",
+		.start	= GPIO_WLAN_IRQ,
+		.end	= GPIO_WLAN_IRQ,
+		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL |
+			  IORESOURCE_IRQ_SHAREABLE,
+	},
+};
+
+struct wifi_gpio_sleep_data {
+	uint num;
+	uint cfg;
+	uint pull;
+};
+
+static struct wifi_gpio_sleep_data manta_sleep_wifi_gpios[] = {
+	/* WLAN_SDIO_CMD */
+	{WLAN_SDIO_CMD, S5P_GPIO_PD_INPUT, S5P_GPIO_PD_UPDOWN_DISABLE},
+	/* WLAN_SDIO_D(0) */
+	{WLAN_SDIO_DATA0, S5P_GPIO_PD_INPUT, S5P_GPIO_PD_UPDOWN_DISABLE},
+	/* WLAN_SDIO_D(1) */
+	{WLAN_SDIO_DATA1, S5P_GPIO_PD_INPUT, S5P_GPIO_PD_UPDOWN_DISABLE},
+	/* WLAN_SDIO_D(2) */
+	{WLAN_SDIO_DATA2, S5P_GPIO_PD_INPUT, S5P_GPIO_PD_UPDOWN_DISABLE},
+	/* WLAN_SDIO_D(3) */
+	{WLAN_SDIO_DATA3, S5P_GPIO_PD_INPUT, S5P_GPIO_PD_UPDOWN_DISABLE},
+};
+
+static void (*wifi_status_cb)(struct platform_device *, int state);
+
+static int exynos5_manta_wlan_ext_cd_init(
+			void (*notify_func)(struct platform_device *, int))
+{
+	wifi_status_cb = notify_func;
+	return 0;
+}
+
+static int exynos5_manta_wlan_ext_cd_cleanup(
+			void (*notify_func)(struct platform_device *, int))
+{
+	wifi_status_cb = NULL;
+	return 0;
+}
+
+static int manta_wifi_set_carddetect(int val)
+{
+	pr_debug("%s: %d\n", __func__, val);
+
+	if (wifi_status_cb)
+		wifi_status_cb(&exynos5_device_dwmci1, val);
+	else
+		pr_warning("%s: Nobody to notify\n", __func__);
+
+	return 0;
+}
+
+static int manta_wifi_power_state = -1;
+
+static int manta_wifi_power(int on)
+{
+	int ret = 0;
+	int sleep_before_on = 600;
+	int sleep_after_on = 500;
+
+	bt_wlan_lock();
+	pr_debug("%s: %d\n", __func__, on);
+
+	if (manta_wifi_power_state == -1) { /* On probe */
+		sleep_before_on = 50;
+		sleep_after_on = 300;
+	}
+
+	if (on)
+		msleep(sleep_before_on);
+	else
+		msleep(50);
+	gpio_set_value(GPIO_WLAN_PMENA, on);
+	if (on)
+		msleep(sleep_after_on);
+	else
+		msleep(50);
+
+	manta_wifi_power_state = on;
+	bt_wlan_unlock();
+	return ret;
+}
+
+static int manta_wifi_reset_state;
+
+static int manta_wifi_reset(int on)
+{
+	pr_debug("%s: do nothing\n", __func__);
+	manta_wifi_reset_state = on;
+	return 0;
+}
+
+static unsigned char manta_mac_addr[IFHWADDRLEN] = { 0, 0x90, 0x4c, 0, 0, 0 };
+
+static int __init manta_mac_addr_setup(char *str)
+{
+	char macstr[IFHWADDRLEN*3];
+	char *macptr = macstr;
+	char *token;
+	int i = 0;
+
+	if (!str)
+		return 0;
+	pr_debug("wlan MAC = %s\n", str);
+	if (strlen(str) >= sizeof(macstr))
+		return 0;
+	strcpy(macstr, str);
+
+	while ((token = strsep(&macptr, ":")) != NULL) {
+		unsigned long val;
+		int res;
+
+		if (i >= IFHWADDRLEN)
+			break;
+		res = kstrtoul(token, 0x10, &val);
+		if (res < 0)
+			return 0;
+		manta_mac_addr[i++] = (u8)val;
+	}
+
+	return 1;
+}
+
+__setup("androidboot.wifimacaddr=", manta_mac_addr_setup);
+
+static int manta_wifi_get_mac_addr(unsigned char *buf)
+{
+	uint rand_mac;
+
+	if (!buf)
+		return -EFAULT;
+
+	if ((manta_mac_addr[4] == 0) && (manta_mac_addr[5] == 0)) {
+		srandom32((uint)jiffies);
+		rand_mac = random32();
+		manta_mac_addr[3] = (unsigned char)rand_mac;
+		manta_mac_addr[4] = (unsigned char)(rand_mac >> 8);
+		manta_mac_addr[5] = (unsigned char)(rand_mac >> 16);
+	}
+	memcpy(buf, manta_mac_addr, IFHWADDRLEN);
+	return 0;
+}
+
+/* Customized Locale table : OPTIONAL feature */
+#define WLC_CNTRY_BUF_SZ	4
+struct cntry_locales_custom {
+	char iso_abbrev[WLC_CNTRY_BUF_SZ];
+	char custom_locale[WLC_CNTRY_BUF_SZ];
+	int  custom_locale_rev;
+};
+
+static struct cntry_locales_custom manta_wifi_translate_custom_table[] = {
+/* Table should be filled out based on custom platform regulatory requirement */
+	{"",   "XY", 9},  /* universal */
+	{"US", "Q2", 32}, /* input ISO "US" to : Q2 regrev 32 */
+	{"CA", "Q2", 32}, /* input ISO "CA" to : Q2 regrev 32 */
+	{"EU", "EU", 51}, /* European union countries */
+	{"AT", "EU", 51},
+	{"BE", "EU", 51},
+	{"BG", "EU", 51},
+	{"CY", "EU", 51},
+	{"CZ", "EU", 51},
+	{"DK", "EU", 51},
+	{"EE", "EU", 51},
+	{"FI", "EU", 51},
+	{"FR", "EU", 51},
+	{"DE", "EU", 51},
+	{"GR", "EU", 51},
+	{"HU", "EU", 51},
+	{"IE", "EU", 51},
+	{"IT", "EU", 51},
+	{"LV", "EU", 51},
+	{"LI", "EU", 51},
+	{"LT", "EU", 51},
+	{"LU", "EU", 51},
+	{"MT", "EU", 51},
+	{"NL", "EU", 51},
+	{"PL", "EU", 51},
+	{"PT", "EU", 51},
+	{"RO", "EU", 51},
+	{"SK", "EU", 51},
+	{"SI", "EU", 51},
+	{"ES", "EU", 51},
+	{"SE", "EU", 51},
+	{"GB", "EU", 51}, /* input ISO "GB" to : EU regrev 51 */
+	{"IL", "IL", 0},
+	{"CH", "CH", 0},
+	{"TR", "TR", 0},
+	{"NO", "NO", 0},
+	{"KR", "KR", 25},
+	{"AU", "XY", 9},
+	{"CN", "CN", 0},
+	{"TW", "XY", 9},
+	{"AR", "XY", 9},
+	{"MX", "XY", 9},
+	{"JP", "EU", 51},
+	{"BR", "KR", 25}
+};
+
+static void *manta_wifi_get_country_code(char *ccode)
+{
+	int size = ARRAY_SIZE(manta_wifi_translate_custom_table);
+	int i;
+
+	if (!ccode)
+		return NULL;
+
+	for (i = 0; i < size; i++)
+		if (strcmp(ccode,
+			manta_wifi_translate_custom_table[i].iso_abbrev) == 0)
+			return &manta_wifi_translate_custom_table[i];
+	return &manta_wifi_translate_custom_table[0];
+}
+
+static struct wifi_platform_data manta_wifi_control = {
+	.set_power		= manta_wifi_power,
+	.set_reset		= manta_wifi_reset,
+	.set_carddetect		= manta_wifi_set_carddetect,
+	.mem_prealloc		= NULL,
+	.get_mac_addr		= manta_wifi_get_mac_addr,
+	.get_country_code	= manta_wifi_get_country_code,
+};
+
+static struct platform_device manta_wifi_device = {
+	.name		= "bcmdhd_wlan",
+	.id		= 1,
+	.num_resources	= ARRAY_SIZE(manta_wifi_resources),
+	.resource	= manta_wifi_resources,
+	.dev		= {
+		.platform_data = &manta_wifi_control,
+	},
+};
+
+static void exynos5_setup_wlan_cfg_gpio(int width)
+{
+	unsigned int gpio;
+
+	/* Set all the necessary GPC2[0:1] pins to special-function 2 */
+	for (gpio = EXYNOS5_GPC2(0); gpio < EXYNOS5_GPC2(2); gpio++) {
+		s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
+		s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+		s5p_gpio_set_drvstr(gpio, S5P_GPIO_DRVSTR_LV2);
+	}
+
+	for (gpio = EXYNOS5_GPC2(3); gpio <= EXYNOS5_GPC2(6); gpio++) {
+		/* Data pin GPC2[3:6] to special-function 2 */
+		s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
+		s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+		s5p_gpio_set_drvstr(gpio, S5P_GPIO_DRVSTR_LV2);
+	}
+}
+
+static struct dw_mci_board exynos_wlan_pdata __initdata = {
+	.num_slots		= 1,
+	.cd_type                = DW_MCI_CD_EXTERNAL,
+	.quirks			= DW_MCI_QUIRK_HIGHSPEED |
+				  DW_MCI_QUIRK_IDMAC_DTO,
+	.bus_hz			= 50 * 1000 * 1000,
+	.max_bus_hz		= 200 * 1000 * 1000,
+	.caps			= MMC_CAP_UHS_SDR104 |
+				  MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED,
+	.caps2			= MMC_CAP2_BROKEN_VOLTAGE,
+	.pm_caps		= MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY,
+	.fifo_depth		= 0x80,
+	.detect_delay_ms	= 0,
+	.hclk_name		= "dwmci",
+	.cclk_name		= "sclk_dwmci",
+	.cfg_gpio		= exynos5_setup_wlan_cfg_gpio,
+	.ext_cd_init		= exynos5_manta_wlan_ext_cd_init,
+	.ext_cd_cleanup		= exynos5_manta_wlan_ext_cd_cleanup,
+	.sdr_timing		= 0x03040002,
+	.ddr_timing		= 0x03030002,
+	.clk_drv 		= 2,
+};
+
+static struct platform_device *manta_wlan_devs[] __initdata = {
+	&exynos5_device_dwmci1,
+	&manta_wifi_device,
+};
+
+static void __init manta_wlan_gpio(void)
+{
+	int gpio;
+	int i;
+
+	pr_debug("%s: start\n", __func__);
+
+	/* Setup wlan Power Enable */
+	gpio = GPIO_WLAN_PMENA;
+	s3c_gpio_cfgpin(gpio, S3C_GPIO_OUTPUT);
+	s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+	s5p_gpio_set_drvstr(gpio, S5P_GPIO_DRVSTR_LV3);
+	/* Keep power state during suspend */
+	s5p_gpio_set_pd_cfg(gpio, S5P_GPIO_PD_PREV_STATE);
+	gpio_set_value(gpio, 0);
+
+	/* Setup wlan IRQ */
+	gpio = GPIO_WLAN_IRQ;
+	s3c_gpio_cfgpin(gpio, S3C_GPIO_INPUT);
+	s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+	s5p_gpio_set_drvstr(gpio, S5P_GPIO_DRVSTR_LV3);
+
+	manta_wifi_resources[0].start = gpio_to_irq(gpio);
+	manta_wifi_resources[0].end = gpio_to_irq(gpio);
+
+	/* Setup sleep GPIO for wifi */
+	for (i = 0; i < ARRAY_SIZE(manta_sleep_wifi_gpios); i++) {
+		gpio = manta_sleep_wifi_gpios[i].num;
+		s5p_gpio_set_pd_cfg(gpio, manta_sleep_wifi_gpios[i].cfg);
+		s5p_gpio_set_pd_pull(gpio, manta_sleep_wifi_gpios[i].pull);
+	}
+
+}
+
+void __init exynos5_manta_wlan_init(void)
+{
+	pr_debug("%s: start\n", __func__);
+
+	exynos_dwmci_set_platdata(&exynos_wlan_pdata, 1);
+	dev_set_name(&exynos5_device_dwmci1.dev, "exynos4-sdhci.1");
+	clk_add_alias("dwmci", "dw_mmc.1", "hsmmc", &exynos5_device_dwmci1.dev);
+	clk_add_alias("sclk_dwmci", "dw_mmc.1", "sclk_mmc",
+		      &exynos5_device_dwmci1.dev);
+	manta_wlan_gpio();
+	platform_add_devices(manta_wlan_devs, ARRAY_SIZE(manta_wlan_devs));
+}
diff --git a/arch/arm/mach-exynos/board-manta.c b/arch/arm/mach-exynos/board-manta.c
new file mode 100644
index 0000000..c6d9e6c
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta.c
@@ -0,0 +1,894 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.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/errno.h>
+#include <linux/cma.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/ion.h>
+#include <linux/i2c.h>
+#include <linux/keyreset.h>
+#include <linux/mmc/host.h>
+#include <linux/memblock.h>
+#include <linux/persistent_ram.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/exynos_usb3_drd.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/sys_soc.h>
+#include <linux/platform_data/stmpe811-adc.h>
+#include <linux/leds-as3668.h>
+
+#include <asm/mach/arch.h>
+#include <asm/hardware/gic.h>
+#include <asm/mach-types.h>
+#include <asm/system_info.h>
+#include <asm/system_misc.h>
+
+#include <plat/adc.h>
+#include <plat/clock.h>
+#include <plat/cpu.h>
+#include <plat/regs-serial.h>
+#include <plat/gpio-cfg.h>
+#include <plat/devs.h>
+#include <plat/iic.h>
+#include <plat/sdhci.h>
+#include <plat/udc-hs.h>
+#include <plat/ehci.h>
+
+#include <mach/map.h>
+#include <mach/sysmmu.h>
+#include <mach/exynos_fiq_debugger.h>
+#include <mach/exynos-ion.h>
+#include <mach/dwmci.h>
+#include <mach/ohci.h>
+#include <mach/tmu.h>
+#include <mach/exynos5_bus.h>
+
+#include "../../../drivers/staging/android/ram_console.h"
+#include "board-manta.h"
+#include "common.h"
+#include "resetreason.h"
+
+#define MANTA_CPU0_DEBUG_PA		0x10890000
+#define MANTA_CPU1_DEBUG_PA		0x10892000
+#define MANTA_CPU_DBGPCSR		0xa0
+
+static int manta_hw_rev;
+phys_addr_t manta_bootloader_fb_start;
+phys_addr_t manta_bootloader_fb_size = 2560 * 1600 * 4;
+static bool manta_charger_mode;
+static void __iomem *manta_cpu0_debug;
+static void __iomem *manta_cpu1_debug;
+
+static int __init s3cfb_bootloaderfb_arg(char *options)
+{
+	char *p = options;
+
+	manta_bootloader_fb_start = memparse(p, &p);
+	pr_debug("bootloader framebuffer found at %8X\n",
+			manta_bootloader_fb_start);
+
+	return 0;
+}
+early_param("s3cfb.bootloaderfb", s3cfb_bootloaderfb_arg);
+
+static int __init manta_androidboot_mode_arg(char *options)
+{
+	if (!strcmp(options, "charger"))
+		manta_charger_mode = true;
+	return 0;
+}
+early_param("androidboot.mode", manta_androidboot_mode_arg);
+
+static struct gpio manta_hw_rev_gpios[] = {
+	{EXYNOS5_GPV1(4), GPIOF_IN, "hw_rev0"},
+	{EXYNOS5_GPV1(3), GPIOF_IN, "hw_rev1"},
+	{EXYNOS5_GPV1(2), GPIOF_IN, "hw_rev2"},
+	{EXYNOS5_GPV1(1), GPIOF_IN, "hw_rev3"},
+};
+
+int exynos5_manta_get_revision(void)
+{
+	return manta_hw_rev;
+}
+
+static char manta_board_info_string[255];
+
+static void manta_init_hw_rev(void)
+{
+	int ret;
+	int i;
+
+	ret = gpio_request_array(manta_hw_rev_gpios,
+		ARRAY_SIZE(manta_hw_rev_gpios));
+
+	BUG_ON(ret);
+
+	for (i = 0; i < ARRAY_SIZE(manta_hw_rev_gpios); i++)
+		manta_hw_rev |= gpio_get_value(manta_hw_rev_gpios[i].gpio) << i;
+
+	snprintf(manta_board_info_string, sizeof(manta_board_info_string) - 1,
+		"Manta HW revision: %d, CPU EXYNOS5250 Rev%d.%d",
+		manta_hw_rev,
+		samsung_rev() >> 4,
+		samsung_rev() & 0xf);
+	pr_info("%s\n", manta_board_info_string);
+	mach_panic_string = manta_board_info_string;
+}
+
+static struct ram_console_platform_data ramconsole_pdata;
+
+static struct platform_device ramconsole_device = {
+	.name           = "ram_console",
+	.id             = -1,
+	.dev		= {
+		.platform_data = &ramconsole_pdata,
+	},
+};
+
+static struct platform_device persistent_trace_device = {
+	.name           = "persistent_trace",
+	.id             = -1,
+};
+
+static struct resource persistent_clock_resource[] = {
+	[0] = DEFINE_RES_MEM(S3C_PA_RTC, SZ_256),
+};
+
+
+static struct platform_device persistent_clock = {
+	.name           = "persistent_clock",
+	.id             = -1,
+	.num_resources	= ARRAY_SIZE(persistent_clock_resource),
+	.resource	= persistent_clock_resource,
+};
+
+/* Following are default values for UCON, ULCON and UFCON UART registers */
+#define MANTA_UCON_DEFAULT	(S3C2410_UCON_TXILEVEL |	\
+				 S3C2410_UCON_RXILEVEL |	\
+				 S3C2410_UCON_TXIRQMODE |	\
+				 S3C2410_UCON_RXIRQMODE |	\
+				 S3C2410_UCON_RXFIFO_TOI |	\
+				 S3C2443_UCON_RXERR_IRQEN)
+
+#define MANTA_ULCON_DEFAULT	S3C2410_LCON_CS8
+
+#define MANTA_UFCON_DEFAULT	(S3C2410_UFCON_FIFOMODE |	\
+				 S5PV210_UFCON_TXTRIG4 |	\
+				 S5PV210_UFCON_RXTRIG4)
+
+static struct s3c2410_uartcfg manta_uartcfgs[] __initdata = {
+	[0] = {
+		.hwport		= 0,
+		.flags		= 0,
+		.ucon		= MANTA_UCON_DEFAULT,
+		.ulcon		= MANTA_ULCON_DEFAULT,
+		.ufcon		= MANTA_UFCON_DEFAULT,
+		.wake_peer	= bcm_bt_lpm_exit_lpm_locked,
+	},
+	[1] = {
+		.hwport		= 1,
+		.flags		= 0,
+		.ucon		= MANTA_UCON_DEFAULT,
+		.ulcon		= MANTA_ULCON_DEFAULT,
+		.ufcon		= MANTA_UFCON_DEFAULT,
+	},
+	/* Do not initialize hwport 2, it will be handled by fiq_debugger */
+	[2] = {
+		.hwport		= 3,
+		.flags		= 0,
+		.ucon		= MANTA_UCON_DEFAULT,
+		.ulcon		= MANTA_ULCON_DEFAULT,
+		.ufcon		= MANTA_UFCON_DEFAULT,
+	},
+};
+
+static struct gpio_event_direct_entry manta_keypad_key_map[] = {
+	{
+		.gpio   = EXYNOS5_GPX2(7),
+		.code   = KEY_POWER,
+		.dev    = 0,
+	},
+	{
+		.gpio   = EXYNOS5_GPX2(0),
+		.code   = KEY_VOLUMEUP,
+		.dev    = 0,
+	},
+	{
+		.gpio   = EXYNOS5_GPX2(1),
+		.code   = KEY_VOLUMEDOWN,
+		.dev    = 0,
+	}
+};
+
+static struct gpio_event_direct_entry manta_switch_map[] = {
+	{
+		.gpio   = EXYNOS5_GPX1(3),
+		.code   = SW_LID,
+		.dev    = 1,
+	}
+};
+
+static struct gpio_event_input_info manta_keypad_key_info = {
+	.info.func              = gpio_event_input_func,
+	.info.no_suspend        = true,
+	.debounce_time.tv64     = 5 * NSEC_PER_MSEC,
+	.type                   = EV_KEY,
+	.keymap                 = manta_keypad_key_map,
+	.keymap_size            = ARRAY_SIZE(manta_keypad_key_map)
+};
+
+static struct gpio_event_input_info manta_switch_info = {
+	.info.func              = gpio_event_input_func,
+	.info.no_suspend        = true,
+	.debounce_time.tv64     = 10 * NSEC_PER_MSEC,
+	.type                   = EV_SW,
+	.keymap                 = manta_switch_map,
+	.keymap_size            = ARRAY_SIZE(manta_switch_map)
+};
+
+static struct gpio_event_info *manta_event_input_info[] = {
+	&manta_keypad_key_info.info,
+	&manta_switch_info.info,
+};
+
+static struct gpio_event_platform_data manta_event_data = {
+	.names  = {
+		"manta-keypad",
+		"manta-switch",
+		NULL,
+	},
+	.info           = manta_event_input_info,
+	.info_count     = ARRAY_SIZE(manta_event_input_info),
+};
+
+static struct platform_device manta_event_device = {
+	.name   = GPIO_EVENT_DEV_NAME,
+	.id     = 0,
+	.dev    = {
+		.platform_data = &manta_event_data,
+	},
+};
+
+static void __init manta_gpio_power_init(void)
+{
+	int err = 0;
+
+	err = gpio_request_one(EXYNOS5_GPX2(7), 0, "GPX2(7)");
+	if (err) {
+		printk(KERN_ERR "failed to request GPX2(7) for "
+				"suspend/resume control\n");
+		return;
+	}
+	s3c_gpio_setpull(EXYNOS5_GPX2(7), S3C_GPIO_PULL_NONE);
+
+	gpio_free(EXYNOS5_GPX2(7));
+}
+
+static int manta_keyreset_fn(void)
+{
+	arm_pm_restart('h', NULL);
+	return 1;
+}
+
+static struct keyreset_platform_data manta_reset_keys_pdata = {
+	.keys_down	= {
+		KEY_POWER,
+		0,
+	},
+	.down_time_ms	= 1500,
+	.reset_fn	= manta_keyreset_fn,
+};
+
+struct platform_device manta_keyreset_device = {
+	.name	= KEYRESET_NAME,
+	.dev	= {
+		.platform_data	= &manta_reset_keys_pdata,
+	},
+};
+
+static struct as3668_platform_data as3668_pdata = {
+	.led_array = {AS3668_RED, AS3668_GREEN, AS3668_BLUE, AS3668_WHITE},
+	.vbat_monitor_voltage_index = AS3668_VMON_VBAT_3_0V,
+	.shutdown_enable = AS3668_SHUTDOWN_ENABLE_OFF,
+	.pattern_start_source = AS3668_PATTERN_START_SOURCE_SW,
+	.pwm_source = AS3668_PWM_SOURCE_INTERNAL,
+	.gpio_input_invert = AS3668_GPIO_INPUT_NONINVERT,
+	.gpio_input_mode = AS3668_GPIO_INPUT_MODE_ANALOG,
+	.gpio_mode = AS3668_GPIO_MODE_INPUT_ONLY,
+	.audio_input_pin = AS3668_AUDIO_CTRL_INPUT_CURR4,
+	.audio_pulldown_off = AS3668_AUDIO_CTRL_PLDN_ENABLE,
+	.audio_adc_characteristic = AS3668_AUDIO_CTRL_ADC_CHAR_250,
+	.audio_dis_start = AS3668_AUDIO_INPUT_CAP_PRECHARGE,
+	.audio_man_start = AS3668_AUDIO_INPUT_AUTO_PRECHARGE,
+};
+
+/* I2C0 */
+static struct i2c_board_info i2c_devs0[] __initdata = {
+	{
+		I2C_BOARD_INFO("exynos_hdcp", (0x74 >> 1)),
+	},
+	{
+		I2C_BOARD_INFO("exynos_edid", (0xA0 >> 1)),
+	},
+};
+
+struct s3c2410_platform_i2c i2c0_data __initdata = {
+	.flags		= 0,
+	.slave_addr	= 0x10,
+	.frequency	= 100*1000,
+	.sda_delay	= 100,
+};
+
+/* I2C1 */
+static struct i2c_board_info i2c_devs1[] __initdata = {
+	{
+		I2C_BOARD_INFO("as3668", 0x42),
+		.platform_data = &as3668_pdata,
+	},
+};
+
+static struct stmpe811_callbacks *stmpe811_cbs;
+static void stmpe811_register_callback(struct stmpe811_callbacks *cb)
+{
+	stmpe811_cbs = cb;
+}
+
+int manta_stmpe811_read_adc_data(u8 channel)
+{
+	if (stmpe811_cbs && stmpe811_cbs->get_adc_data)
+		return stmpe811_cbs->get_adc_data(channel);
+
+	return -EINVAL;
+}
+
+struct stmpe811_platform_data stmpe811_pdata = {
+	.register_cb = stmpe811_register_callback,
+};
+
+/* ADC */
+static struct s3c_adc_platdata manta_adc_data __initdata = {
+	.phy_init       = s3c_adc_phy_init,
+	.phy_exit       = s3c_adc_phy_exit,
+};
+
+/* I2C2 */
+static struct i2c_board_info i2c_devs2[] __initdata = {
+	{
+		I2C_BOARD_INFO("stmpe811-adc", (0x82 >> 1)),
+		.platform_data  = &stmpe811_pdata,
+	},
+};
+
+/* TMU */
+static struct tmu_data manta_tmu_pdata __initdata = {
+	.ts = {
+		.stop_throttle		= 78,
+		.start_throttle		= 80,
+		.start_tripping		= 110,
+		.start_emergency	= 120,
+		.stop_mem_throttle	= 80,
+		.start_mem_throttle	= 85,
+	},
+
+	.efuse_value	= 80,
+	.slope		= 0x10608802,
+};
+
+static struct persistent_ram_descriptor manta_prd[] __initdata = {
+	{
+		.name = "ram_console",
+		.size = SZ_2M,
+	},
+#ifdef CONFIG_PERSISTENT_TRACER
+	{
+		.name = "persistent_trace",
+		.size = SZ_1M,
+	},
+#endif
+};
+
+static struct persistent_ram manta_pr __initdata = {
+	.descs = manta_prd,
+	.num_descs = ARRAY_SIZE(manta_prd),
+	.start = PLAT_PHYS_OFFSET + SZ_1G + SZ_512M,
+#ifdef CONFIG_PERSISTENT_TRACER
+	.size = 3 * SZ_1M,
+#else
+	.size = SZ_2M,
+#endif
+};
+
+
+/* defined in arch/arm/mach-exynos/reserve-mem.c */
+extern void exynos_cma_region_reserve(struct cma_region *,
+				struct cma_region *, size_t, const char *);
+extern int kbase_carveout_mem_reserve(phys_addr_t size);
+
+static void __init exynos_reserve_mem(void)
+{
+	static struct cma_region regions[] = {
+		{
+			.name = "ion",
+#ifdef CONFIG_ION_EXYNOS_CONTIGHEAP_SIZE
+			.size = CONFIG_ION_EXYNOS_CONTIGHEAP_SIZE * SZ_1K,
+#endif
+			{
+				.alignment = SZ_1M
+			}
+		},
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+#ifdef CONFIG_ION_EXYNOS_DRM_MFC_SH
+		{
+			.name = "drm_mfc_sh",
+			.size = SZ_1M,
+			.alignment = SZ_1M,
+		},
+#endif
+#ifdef CONFIG_ION_EXYNOS_DRM_MSGBOX_SH
+		{
+			.name = "drm_msgbox_sh",
+			.size = SZ_1M,
+			.alignment = SZ_1M,
+		},
+#endif
+#endif
+		{
+			.size = 0 /* END OF REGION DEFINITIONS */
+		}
+	};
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+       static struct cma_region regions_secure[] = {
+#ifdef CONFIG_ION_EXYNOS_DRM_MEMSIZE_FIMD_VIDEO
+	       {
+		       .name = "drm_fimd_video",
+		       .size = CONFIG_ION_EXYNOS_DRM_MEMSIZE_FIMD_VIDEO *
+			       SZ_1K,
+		       .alignment = SZ_1M,
+	       },
+#endif
+#ifdef CONFIG_ION_EXYNOS_DRM_MEMSIZE_MFC_OUTPUT
+	       {
+		       .name = "drm_mfc_output",
+		       .size = CONFIG_ION_EXYNOS_DRM_MEMSIZE_MFC_OUTPUT *
+			       SZ_1K,
+		       .alignment = SZ_1M,
+	       },
+#endif
+#ifdef CONFIG_ION_EXYNOS_DRM_MEMSIZE_MFC_INPUT
+	       {
+		       .name = "drm_mfc_input",
+		       .size = CONFIG_ION_EXYNOS_DRM_MEMSIZE_MFC_INPUT *
+			       SZ_1K,
+		       .alignment = SZ_1M,
+	       },
+#endif
+#ifdef CONFIG_ION_EXYNOS_DRM_MFC_FW
+               {
+                       .name = "drm_mfc_fw",
+                       .size = SZ_1M,
+		       .alignment = SZ_1M,
+               },
+#endif
+#ifdef CONFIG_ION_EXYNOS_DRM_SECTBL
+               {
+                       .name = "drm_sectbl",
+                       .size = SZ_1M,
+		       .alignment = SZ_1M,
+               },
+#endif
+               {
+                       .size = 0
+               },
+	};
+#else /* !CONFIG_EXYNOS_CONTENT_PATH_PROTECTION */
+	struct cma_region *regions_secure = NULL;
+#endif /* CONFIG_EXYNOS_CONTENT_PATH_PROTECTION */
+
+       static const char map[] __initconst =
+#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
+		"ion-exynos/mfc_sh=drm_mfc_sh;"
+		"ion-exynos/msgbox_sh=drm_msgbox_sh;"
+		"ion-exynos/fimd_video=drm_fimd_video;"
+		"ion-exynos/mfc_output=drm_mfc_output;"
+		"ion-exynos/mfc_input=drm_mfc_input;"
+		"ion-exynos/mfc_fw=drm_mfc_fw;"
+		"ion-exynos/sectbl=drm_sectbl;"
+		"s5p-smem/mfc_sh=drm_mfc_sh;"
+		"s5p-smem/msgbox_sh=drm_msgbox_sh;"
+		"s5p-smem/fimd_video=drm_fimd_video;"
+		"s5p-smem/mfc_output=drm_mfc_output;"
+		"s5p-smem/mfc_input=drm_mfc_input;"
+		"s5p-smem/mfc_fw=drm_mfc_fw;"
+		"s5p-smem/sectbl=drm_sectbl;"
+#endif
+		"ion-exynos=ion;"
+		"s5p-mfc-v6/f=fw;"
+		"s5p-mfc-v6/a=b1;";
+
+	persistent_ram_early_init(&manta_pr);
+	if (manta_bootloader_fb_start) {
+		int err = memblock_reserve(manta_bootloader_fb_start,
+				manta_bootloader_fb_size);
+		if (err)
+			pr_warn("failed to reserve old framebuffer location\n");
+	} else {
+		pr_warn("bootloader framebuffer start address not set\n");
+	}
+
+	exynos_cma_region_reserve(regions, regions_secure, 0, map);
+	kbase_carveout_mem_reserve(384 * SZ_1M);
+	ion_reserve(&exynos_ion_pdata);
+}
+
+static void exynos_dwmci0_cfg_gpio(int width)
+{
+	unsigned int gpio;
+
+	for (gpio = EXYNOS5_GPC0(0); gpio < EXYNOS5_GPC0(2); gpio++) {
+		s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
+		s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+		s5p_gpio_set_drvstr(gpio, S5P_GPIO_DRVSTR_LV4);
+	}
+
+	switch (width) {
+	case 8:
+		for (gpio = EXYNOS5_GPC1(0); gpio <= EXYNOS5_GPC1(3); gpio++) {
+			s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
+			s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+			s5p_gpio_set_drvstr(gpio, S5P_GPIO_DRVSTR_LV4);
+		}
+	case 4:
+		for (gpio = EXYNOS5_GPC0(3); gpio <= EXYNOS5_GPC0(6); gpio++) {
+			s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
+			s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+			s5p_gpio_set_drvstr(gpio, S5P_GPIO_DRVSTR_LV4);
+		}
+		break;
+	case 1:
+		gpio = EXYNOS5_GPC0(3);
+		s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
+		s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+		s5p_gpio_set_drvstr(gpio, S5P_GPIO_DRVSTR_LV4);
+	default:
+		break;
+	}
+}
+
+static void exynos_dwmci0_hw_reset(u32 slot_id)
+{
+	unsigned int emmc_en, gpio;
+
+	if (slot_id != 0)
+		return;
+
+	emmc_en = EXYNOS5_GPC0(2);
+	s3c_gpio_cfgpin(emmc_en, S3C_GPIO_OUTPUT);
+	s3c_gpio_setpull(emmc_en, S3C_GPIO_PULL_NONE);
+
+	/* eMMC Card Power Off */
+	for (gpio = EXYNOS5_GPC0(0); gpio < EXYNOS5_GPC0(2); gpio++) {
+		s3c_gpio_cfgpin(gpio, S3C_GPIO_OUTPUT);
+		s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+		gpio_set_value(gpio, 0);
+	}
+	for (gpio = EXYNOS5_GPC1(0); gpio <= EXYNOS5_GPC1(3); gpio++) {
+		s3c_gpio_cfgpin(gpio, S3C_GPIO_OUTPUT);
+		s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+		gpio_set_value(gpio, 0);
+	}
+	gpio_set_value(emmc_en, 0);
+
+	/* waiting ramp down time for certainly power off */
+	msleep(100);
+
+	/* eMMC Card Power On */
+	for (gpio = EXYNOS5_GPC0(0); gpio < EXYNOS5_GPC0(2); gpio++) {
+		s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
+		s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+		gpio_set_value(gpio, 0);
+	}
+	for (gpio = EXYNOS5_GPC1(0); gpio <= EXYNOS5_GPC1(3); gpio++) {
+		s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
+		s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+		gpio_set_value(gpio, 0);
+	}
+	gpio_set_value(emmc_en, 1);
+
+	/* waiting ramp up time for certainly power on */
+	msleep(50);
+}
+
+static struct dw_mci_board exynos_dwmci0_pdata __initdata = {
+	.num_slots		= 1,
+	.quirks			= DW_MCI_QUIRK_BROKEN_CARD_DETECTION |
+				  DW_MCI_QUIRK_HIGHSPEED |
+                                  DW_MMC_QUIRK_HW_RESET_PW |
+				  DW_MCI_QUIRK_NO_DETECT_EBIT,
+	.bus_hz			= 50 * 1000 * 1000,
+	.max_bus_hz		= 200 * 1000 * 1000,
+	.caps			= MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
+				  MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23 | MMC_CAP_ERASE |
+				  MMC_CAP_HW_RESET,
+	.caps2 			= MMC_CAP2_HS200_1_8V_SDR,
+	.fifo_depth             = 0x80,
+	.detect_delay_ms	= 200,
+	.hclk_name		= "dwmci",
+	.cclk_name		= "sclk_dwmci",
+	.cfg_gpio		= exynos_dwmci0_cfg_gpio,
+	.hw_reset		= exynos_dwmci0_hw_reset,
+	.sdr_timing		= 0x03020001,
+	.ddr_timing		= 0x03030002,
+	.clk_drv		= 0x3,
+};
+
+/* DEVFREQ controlling mif */
+static struct exynos5_bus_mif_platform_data manta_bus_mif_platform_data;
+
+static struct platform_device exynos_bus_mif_devfreq = {
+	.name                   = "exynos5-bus-mif",
+	.dev = {
+		.platform_data	= &manta_bus_mif_platform_data,
+	},
+};
+
+/* DEVFREQ controlling int */
+static struct platform_device exynos_bus_int_devfreq = {
+	.name                   = "exynos5-bus-int",
+};
+
+static struct platform_device *manta_devices[] __initdata = {
+	&ramconsole_device,
+	&persistent_trace_device,
+	&persistent_clock,
+	&s3c_device_i2c0,
+	&s3c_device_i2c1,
+	&s3c_device_i2c2,
+	&s3c_device_i2c3,
+	&s3c_device_i2c4,
+	&s3c_device_i2c5,
+	&s3c_device_i2c7,
+	&s3c_device_adc,
+	&s3c_device_wdt,
+	&exynos5_device_dwmci0,
+	&exynos_device_ion,
+	&exynos_device_tmu,
+	&s3c_device_usb_hsotg,
+	&s5p_device_ehci,
+	&exynos4_device_ohci,
+	&exynos_bus_mif_devfreq,
+	&exynos_bus_int_devfreq,
+	&exynos5_device_g3d,
+};
+
+static struct s3c_hsotg_plat manta_hsotg_pdata;
+
+static void __init manta_udc_init(void)
+{
+	struct s3c_hsotg_plat *pdata = &manta_hsotg_pdata;
+
+	s3c_hsotg_set_platdata(pdata);
+}
+
+static void __init manta_dwmci_init(void)
+{
+	exynos_dwmci_set_platdata(&exynos_dwmci0_pdata, 0);
+	dev_set_name(&exynos5_device_dwmci0.dev, "exynos4-sdhci.0");
+	clk_add_alias("dwmci", "dw_mmc.0", "hsmmc", &exynos5_device_dwmci0.dev);
+	clk_add_alias("sclk_dwmci", "dw_mmc.0", "sclk_mmc",
+		      &exynos5_device_dwmci0.dev);
+}
+
+static void __init manta_map_io(void)
+{
+	clk_xusbxti.rate = 24000000;
+	clk_xxti.rate = 24000000;
+	exynos_init_io(NULL, 0);
+	s3c24xx_init_clocks(clk_xusbxti.rate);
+	s3c24xx_init_uarts(manta_uartcfgs, ARRAY_SIZE(manta_uartcfgs));
+}
+
+static void __init manta_sysmmu_init(void)
+{
+}
+
+static void __init manta_init_early(void)
+{
+}
+
+static void __init soc_info_populate(struct soc_device_attribute *soc_dev_attr)
+{
+	soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%08x%08x\n",
+			                 system_serial_high, system_serial_low);
+	soc_dev_attr->machine = kasprintf(GFP_KERNEL, "Exynos 5250\n");
+	soc_dev_attr->family = kasprintf(GFP_KERNEL, "Exynos 5\n");
+	soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d.%d\n",
+					   samsung_rev() >> 4,
+					   samsung_rev() & 0xf);
+}
+
+static ssize_t manta_get_board_revision(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	return sprintf(buf, "%d\n", manta_hw_rev);
+}
+
+struct device_attribute manta_soc_attr =
+	__ATTR(board_rev,  S_IRUGO, manta_get_board_revision,  NULL);
+
+static void __init exynos5_manta_sysfs_soc_init(void)
+{
+	struct device *parent;
+	struct soc_device *soc_dev;
+	struct soc_device_attribute *soc_dev_attr;
+
+	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+	if (!soc_dev_attr) {
+		printk(KERN_ERR "Failed to allocate memory for soc_dev_attr\n");
+		return;
+	}
+
+	soc_info_populate(soc_dev_attr);
+
+	soc_dev = soc_device_register(soc_dev_attr);
+	if (IS_ERR_OR_NULL(soc_dev)) {
+		kfree(soc_dev_attr);
+		printk(KERN_ERR "Failed to register a soc device under /sys\n");
+		return;
+	}
+
+	parent = soc_device_to_device(soc_dev);
+	if (!IS_ERR_OR_NULL(parent))
+		device_create_file(parent, &manta_soc_attr);
+
+	return;  /* Or return parent should you need to use one later */
+}
+
+/* USB EHCI */
+static struct s5p_ehci_platdata exynos5_manta_ehci_pdata;
+
+static void __init exynos5_manta_ehci_init(void)
+{
+	struct s5p_ehci_platdata *pdata = &exynos5_manta_ehci_pdata;
+
+	s5p_ehci_set_platdata(pdata);
+}
+
+/* USB OHCI */
+static struct exynos4_ohci_platdata exynos5_manta_ohci_pdata;
+
+static void __init exynos5_manta_ohci_init(void)
+{
+	struct exynos4_ohci_platdata *pdata = &exynos5_manta_ohci_pdata;
+
+	exynos4_ohci_set_platdata(pdata);
+}
+
+void manta_panic_dump_cpu_pc(int cpu, unsigned long dbgpcsr)
+{
+	void *pc = NULL;
+
+	pr_err("CPU%d DBGPCSR: %08lx\n", cpu, dbgpcsr);
+	if ((dbgpcsr & 3) == 0)
+		pc = (void *)(dbgpcsr - 8);
+	else if ((dbgpcsr & 1) == 1)
+		pc = (void *)((dbgpcsr & ~1) - 4);
+
+	pr_err("CPU%d PC: <%p> %pF\n", cpu, pc, pc);
+}
+
+int manta_panic_notify(struct notifier_block *nb, unsigned long event, void *p)
+{
+	unsigned long dbgpcsr;
+
+	if (manta_cpu0_debug && cpu_online(0)) {
+		dbgpcsr = __raw_readl(manta_cpu0_debug + MANTA_CPU_DBGPCSR);
+		manta_panic_dump_cpu_pc(0, dbgpcsr);
+	}
+	if (manta_cpu1_debug && cpu_online(1)) {
+		dbgpcsr = __raw_readl(manta_cpu1_debug + MANTA_CPU_DBGPCSR);
+		manta_panic_dump_cpu_pc(1, dbgpcsr);
+	}
+	return NOTIFY_OK;
+}
+
+struct notifier_block manta_panic_nb = {
+	.notifier_call = manta_panic_notify,
+};
+
+static void __init manta_panic_init(void)
+{
+	manta_cpu0_debug = ioremap(MANTA_CPU0_DEBUG_PA, SZ_4K);
+	manta_cpu1_debug = ioremap(MANTA_CPU1_DEBUG_PA, SZ_4K);
+
+	atomic_notifier_chain_register(&panic_notifier_list, &manta_panic_nb);
+}
+
+static void __init manta_machine_init(void)
+{
+	manta_init_hw_rev();
+
+	if (manta_hw_rev <= MANTA_REV_DOGFOOD02)
+		manta_bus_mif_platform_data.max_freq = 667000;
+
+	exynos_serial_debug_init(2, 0);
+	manta_panic_init();
+
+	manta_gpio_power_init();
+	platform_device_register(&manta_event_device);
+
+	manta_sysmmu_init();
+	manta_dwmci_init();
+
+	if (manta_charger_mode)
+		platform_device_register(&manta_keyreset_device);
+
+	s3c_i2c0_set_platdata(&i2c0_data);
+	s3c_i2c1_set_platdata(NULL);
+	s3c_i2c2_set_platdata(NULL);
+	s3c_i2c3_set_platdata(NULL);
+	s3c_i2c4_set_platdata(NULL);
+	s3c_i2c5_set_platdata(NULL);
+	s3c_i2c7_set_platdata(NULL);
+
+	i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
+	i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
+	if (exynos5_manta_get_revision() <= MANTA_REV_LUNCHBOX)
+		i2c_register_board_info(2, i2c_devs2, ARRAY_SIZE(i2c_devs2));
+	else
+		s3c_adc_set_platdata(&manta_adc_data);
+
+	exynos_tmu_set_platdata(&manta_tmu_pdata);
+
+	manta_udc_init();
+	exynos5_manta_ehci_init();
+	exynos5_manta_ohci_init();
+	ramconsole_pdata.bootinfo = exynos_get_resetreason();
+	platform_add_devices(manta_devices, ARRAY_SIZE(manta_devices));
+
+	exynos5_manta_power_init();
+	exynos5_manta_display_init();
+	exynos5_manta_input_init();
+	exynos5_manta_battery_init();
+	exynos5_manta_pogo_init();
+	exynos5_manta_wlan_init();
+	exynos5_manta_audio_init();
+	exynos5_manta_media_init();
+	exynos5_manta_camera_init();
+	exynos5_manta_sensors_init();
+	exynos5_manta_gps_init();
+	exynos5_manta_jack_init();
+	exynos5_manta_vib_init();
+	exynos5_manta_sysfs_soc_init();
+	exynos5_manta_nfc_init();
+	exynos5_manta_bt_init();
+	exynos5_manta_connector_init();
+	exynos5_manta_adjust_mif_asv_table();
+}
+
+MACHINE_START(MANTA, "Manta")
+	.atag_offset	= 0x100,
+	.init_early	= manta_init_early,
+	.init_irq	= exynos5_init_irq,
+	.map_io		= manta_map_io,
+	.handle_irq	= gic_handle_irq,
+	.init_machine	= manta_machine_init,
+	.timer		= &exynos4_timer,
+	.restart	= exynos5_restart,
+	.reserve	= exynos_reserve_mem,
+MACHINE_END
diff --git a/arch/arm/mach-exynos/board-manta.h b/arch/arm/mach-exynos/board-manta.h
new file mode 100755
index 0000000..b9bc177
--- /dev/null
+++ b/arch/arm/mach-exynos/board-manta.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __MACH_EXYNOS_BOARD_MANTA_H
+#define __MACH_EXYNOS_BOARD_MANTA_H
+
+#include <mach/irqs.h>
+#include <linux/serial_core.h>
+
+#define MANTA_REV_LUNCHBOX	0x1
+#define MANTA_REV_PRE_ALPHA	0x2
+#define MANTA_REV_ALPHA		0x3
+#define MANTA_REV_BETA		0x4
+#define MANTA_REV_DOGFOOD01	0x5
+#define MANTA_REV_DOGFOOD02	0x6
+#define MANTA_REV_DOGFOOD03	0x7
+#define MANTA_REV_DOGFOOD04	0x8
+#define MANTA_REV_DOGFOOD05	0x9
+
+/* board IRQ allocations */
+#define MANTA_IRQ_BOARD_PMIC_START	IRQ_BOARD_START
+#define MANTA_IRQ_BOARD_PMIC_NR		16
+#define MANTA_IRQ_BOARD_AUDIO_START	(IRQ_BOARD_START + \
+					MANTA_IRQ_BOARD_PMIC_NR)
+#define MANTA_IRQ_BOARD_AUDIO_NR	27
+
+/* Manta-specific charger info */
+
+enum manta_charge_source {
+	MANTA_CHARGE_SOURCE_NONE,
+	MANTA_CHARGE_SOURCE_UNKNOWN,
+	MANTA_CHARGE_SOURCE_USB,
+	MANTA_CHARGE_SOURCE_AC_OTHER,
+	MANTA_CHARGE_SOURCE_AC_SAMSUNG,
+};
+
+
+void exynos5_manta_audio_init(void);
+void exynos5_manta_display_init(void);
+void exynos5_manta_input_init(void);
+void exynos5_manta_power_init(void);
+void exynos5_manta_battery_init(void);
+void exynos5_manta_pogo_init(void);
+void exynos5_manta_wlan_init(void);
+void exynos5_manta_media_init(void);
+void exynos5_manta_camera_init(void);
+void exynos5_manta_sensors_init(void);
+void exynos5_manta_gps_init(void);
+void exynos5_manta_jack_init(void);
+void exynos5_manta_vib_init(void);
+void exynos5_manta_nfc_init(void);
+void exynos5_manta_bt_init(void);
+void exynos5_manta_connector_init(void);
+
+int exynos5_manta_get_revision(void);
+int manta_stmpe811_read_adc_data(u8 channel);
+extern int manta_bat_otg_enable(bool enable);
+void manta_otg_set_usb_state(bool connected);
+int manta_pogo_set_vbus(bool status, enum manta_charge_source *charge_source);
+extern int manta_pogo_charge_detect_start(bool spdif_mode_and_gpio_in);
+extern void manta_pogo_charge_detect_end(void);
+void bcm_bt_lpm_exit_lpm_locked(struct uart_port *uport);
+void exynos5_manta_adjust_mif_asv_table(void);
+void manta_force_update_pogo_charger(void);
+
+#endif
diff --git a/arch/arm/mach-exynos/dev-audio.c b/arch/arm/mach-exynos/dev-audio.c
index 79c0f57..96896bd 100644
--- a/arch/arm/mach-exynos/dev-audio.c
+++ b/arch/arm/mach-exynos/dev-audio.c
@@ -47,7 +47,7 @@
 	struct exynos_gpio_cfg exynos5_cfg[3] = {
 				{ EXYNOS5_GPZ(0),  7, S3C_GPIO_SFN(2) },
 				{ EXYNOS5_GPB0(0), 5, S3C_GPIO_SFN(2) },
-				{ EXYNOS5_GPB1(0), 5, S3C_GPIO_SFN(2) }
+				{ EXYNOS5_GPB1(0), 1, S3C_GPIO_SFN(2) }
 	};
 
 	if (pdev->id < 0 || pdev->id > 2) {
@@ -204,7 +204,7 @@
 	struct exynos_gpio_cfg exynos5_cfg[3] = {
 				{ EXYNOS5_GPZ(0),  5, S3C_GPIO_SFN(3) },
 				{ EXYNOS5_GPB0(0), 5, S3C_GPIO_SFN(3) },
-				{ EXYNOS5_GPB1(0), 5, S3C_GPIO_SFN(3) }
+				{ EXYNOS5_GPB1(0), 1, S3C_GPIO_SFN(3) }
 	};
 
 	if (pdev->id < 0 || pdev->id > 2) {
@@ -369,7 +369,7 @@
 {
 	/* configure GPIO for SPDIF port */
 	if (soc_is_exynos5250())
-		s3c_gpio_cfgpin_range(EXYNOS5_GPB1(0), 2, S3C_GPIO_SFN(4));
+		s3c_gpio_cfgpin_range(EXYNOS5_GPB1(0), 1, S3C_GPIO_SFN(4));
 
 	else /* EXYNOS4210, EXYNOS4212 and EXYNOS4412 */
 		s3c_gpio_cfgpin_range(EXYNOS4_GPC1(0), 2, S3C_GPIO_SFN(4));
diff --git a/arch/arm/mach-exynos/mach-smdk5250.c b/arch/arm/mach-exynos/mach-smdk5250.c
index f4f0dfd..f8517d8 100644
--- a/arch/arm/mach-exynos/mach-smdk5250.c
+++ b/arch/arm/mach-exynos/mach-smdk5250.c
@@ -919,8 +919,7 @@
 	.max_bus_hz		= 200 * 1000 * 1000,
 	.caps			= MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
 				  MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
-	.caps2			= MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_PACKED_WR,
-	.desc_sz		= 4,
+	.caps2			= MMC_CAP2_HS200_1_8V_SDR,
 	.fifo_depth             = 0x80,
 	.detect_delay_ms	= 200,
 	.hclk_name		= "dwmci",
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c
index bff00b3..9c21aff 100644
--- a/arch/arm/mach-exynos/pm.c
+++ b/arch/arm/mach-exynos/pm.c
@@ -48,6 +48,7 @@
 #define REG_INFORM1            (EXYNOS_INFORM1)
 #endif
 
+#define EXYNOS_USB20PHY_CFG	(S3C_VA_SYS + 0x230)
 #define EXYNOS_I2C_CFG		(S3C_VA_SYS + 0x234)
 
 #define EXYNOS_WAKEUP_STAT_EINT		(1 << 0)
@@ -98,6 +99,8 @@
 	SAVE_ITEM(S5P_SROM_BC2),
 	SAVE_ITEM(S5P_SROM_BC3),
 
+	/* USB 2.0 PHY CFG */
+	SAVE_ITEM(EXYNOS_USB20PHY_CFG),
 	/* I2C CFG */
 	SAVE_ITEM(EXYNOS_I2C_CFG),
 };
diff --git a/arch/arm/mach-exynos/setup-usb-phy.c b/arch/arm/mach-exynos/setup-usb-phy.c
index 9a76110..f342e48 100644
--- a/arch/arm/mach-exynos/setup-usb-phy.c
+++ b/arch/arm/mach-exynos/setup-usb-phy.c
@@ -292,8 +292,6 @@
 		return 0;
 	}
 
-	exynos_usb_mux_change(pdev, 1);
-
 	exynos_usb_phy_control(USB_PHY1, PHY_ENABLE);
 
 	/* Host and Device should be set at the same time */
@@ -383,6 +381,12 @@
 	writel(hostphy_ctrl0, EXYNOS5_PHY_HOST_CTRL0);
 
 	otgphy_sys = readl(EXYNOS5_PHY_OTG_SYS);
+
+	/* Issue a OTG_SYS_PHYLINK_SW_RESET to release pulldowns on D+/D- */
+	writel(otgphy_sys | OTG_SYS_PHYLINK_SW_RESET, EXYNOS5_PHY_OTG_SYS);
+	udelay(10);
+	writel(otgphy_sys, EXYNOS5_PHY_OTG_SYS);
+
 	otgphy_sys |= (OTG_SYS_FORCE_SUSPEND |
 			OTG_SYS_SIDDQ_UOTG |
 			OTG_SYS_FORCE_SLEEP);
@@ -393,13 +397,114 @@
 	return 0;
 }
 
-static int exynos_usb_dev_phy20_init(struct platform_device *pdev)
+static int s5p_usb_otg_phy_tune(struct s3c_hsotg_plat *pdata, int def_mode)
+{
+	u32 phytune;
+
+	if (!pdata)
+		return -EINVAL;
+
+	pr_debug("usb: %s read original tune\n", __func__);
+	phytune = readl(EXYNOS5_PHY_OTG_TUNE);
+	if (!pdata->def_phytune) {
+		pdata->def_phytune = phytune;
+		pr_debug("usb: %s save default phytune (0x%x)\n",
+				__func__, pdata->def_phytune);
+	}
+
+	pr_debug("usb: %s original tune=0x%x\n",
+			__func__, phytune);
+
+	pr_debug("usb: %s tune_mask=0x%x, tune=0x%x\n",
+			__func__, pdata->phy_tune_mask, pdata->phy_tune);
+
+	if (pdata->phy_tune_mask) {
+		if (def_mode) {
+			pr_debug("usb: %s set defult tune=0x%x\n",
+					__func__, pdata->def_phytune);
+			writel(pdata->def_phytune, EXYNOS5_PHY_OTG_TUNE);
+		} else {
+			phytune &= ~(pdata->phy_tune_mask);
+			phytune |= pdata->phy_tune;
+			udelay(10);
+			pr_debug("usb: %s custom tune=0x%x\n",
+					__func__, phytune);
+			writel(phytune, EXYNOS5_PHY_OTG_TUNE);
+		}
+		phytune = readl(EXYNOS5_PHY_OTG_TUNE);
+		pr_debug("usb: %s modified tune=0x%x\n",
+				__func__, phytune);
+	} else {
+		pr_debug("usb: %s default tune\n", __func__);
+	}
+
+	return 0;
+}
+
+static void set_exynos5_usb_host_phy_tune(void)
+{
+	u32 phytune;
+
+	phytune = readl(EXYNOS5_PHY_HOST_TUNE0);
+	pr_debug("usb: %s old phy tune for host =0x%x\n",
+			__func__, phytune);
+
+	/* sqrxtune [14:12] 3b110 : -15% */
+	phytune &= ~HOST_TUNE0_SQRXTUNE(0x7);
+	phytune |= HOST_TUNE0_SQRXTUNE(0x6);
+	udelay(10);
+	writel(phytune, EXYNOS5_PHY_HOST_TUNE0);
+	phytune = readl(EXYNOS5_PHY_HOST_TUNE0);
+
+	pr_debug("usb: %s new phy tune for host =0x%x\n",
+				__func__, phytune);
+}
+
+static void set_exynos5_usb_device_phy_tune(void)
+{
+	u32 phytune;
+
+	phytune = readl(EXYNOS5_PHY_OTG_TUNE);
+	pr_debug("usb: %s old phy tune for device =0x%x\n",
+				__func__, phytune);
+
+	/* sqrxtune [13:11] 3b110 : -15% */
+	phytune &= ~OTG_TUNE_SQRXTUNE(0x7);
+	phytune |= OTG_TUNE_SQRXTUNE(0x6);
+	/* txpreempamptune [22:21] 2b10 : 2X */
+	phytune &= ~OTG_TUNE_TXPREEMPAMPTUNE(0x3);
+	phytune |= OTG_TUNE_TXPREEMPAMPTUNE(0x2);
+	/* txvreftune [ 3: 0] 8b1000 : +10% */
+	phytune &= ~OTG_TUNE_TXVREFTUNE(0xf);
+	phytune |= OTG_TUNE_TXVREFTUNE(0x8);
+	udelay(10);
+	writel(phytune, EXYNOS5_PHY_OTG_TUNE);
+	phytune = readl(EXYNOS5_PHY_OTG_TUNE);
+
+	pr_debug("usb: %s new phy tune for device =0x%x\n",
+				__func__, phytune);
+}
+
+static int exynos5_usb_host_phy20_init(struct platform_device *pdev)
 {
 	if (exynos_usb_phy_clock_enable(pdev))
 		return -EINVAL;
 
+	/* usb mode change from device to host */
+	exynos_usb_mux_change(pdev, 1);
+
 	exynos5_usb_phy20_init(pdev);
 
+	/* usb host phy tune */
+	set_exynos5_usb_host_phy_tune();
+
+	return 0;
+}
+
+static int exynos5_usb_host_phy20_exit(struct platform_device *pdev)
+{
+	exynos5_usb_phy20_exit(pdev);
+
 	/* usb mode change from host to device */
 	exynos_usb_mux_change(pdev, 0);
 
@@ -408,19 +513,45 @@
 	return 0;
 }
 
-static int exynos_usb_dev_phy20_exit(struct platform_device *pdev)
+static int exynos_usb_dev_phy20_init(struct platform_device *pdev)
 {
+	int ret = 0;
+
 	if (exynos_usb_phy_clock_enable(pdev))
 		return -EINVAL;
 
-	exynos5_usb_phy20_exit(pdev);
+	/* usb mode change from host to device */
+	exynos_usb_mux_change(pdev, 0);
 
-	/* usb mode change from device to host */
-	exynos_usb_mux_change(pdev, 1);
+	exynos5_usb_phy20_init(pdev);
+
+	/* usb device phy tune */
+	set_exynos5_usb_device_phy_tune();
+	/* set custom usb device phy tune */
+	if (pdev->dev.platform_data)
+		ret = s5p_usb_otg_phy_tune(pdev->dev.platform_data, 0);
 
 	exynos_usb_phy_clock_disable(pdev);
 
-	return 0;
+	return ret;
+}
+
+static int exynos_usb_dev_phy20_exit(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	if (exynos_usb_phy_clock_enable(pdev))
+		return -EINVAL;
+
+	/* set custom usb device phy tune */
+	if (pdev->dev.platform_data)
+		ret = s5p_usb_otg_phy_tune(pdev->dev.platform_data, 1);
+
+	exynos5_usb_phy20_exit(pdev);
+
+	exynos_usb_phy_clock_disable(pdev);
+
+	return ret;
 }
 
 static int exynos5_usb_phy30_init(struct platform_device *pdev)
@@ -484,104 +615,17 @@
 	return 0;
 }
 
-static int s5p_usb_otg_phy_tune(struct s3c_hsotg_plat *pdata, int def_mode)
-{
-	u32 phytune;
-
-	if (!pdata)
-		return -EINVAL;
-
-	pr_debug("usb: %s read original tune\n", __func__);
-	phytune = readl(EXYNOS5_PHY_OTG_TUNE);
-	if (!pdata->def_phytune) {
-		pdata->def_phytune = phytune;
-		pr_debug("usb: %s save default phytune (0x%x)\n",
-				__func__, pdata->def_phytune);
-	}
-
-	pr_debug("usb: %s original tune=0x%x\n",
-			__func__, phytune);
-
-	pr_debug("usb: %s tune_mask=0x%x, tune=0x%x\n",
-			__func__, pdata->phy_tune_mask, pdata->phy_tune);
-
-	if (pdata->phy_tune_mask) {
-		if (def_mode) {
-			pr_debug("usb: %s set defult tune=0x%x\n",
-					__func__, pdata->def_phytune);
-			writel(pdata->def_phytune, EXYNOS5_PHY_OTG_TUNE);
-		} else {
-			phytune &= ~(pdata->phy_tune_mask);
-			phytune |= pdata->phy_tune;
-			udelay(10);
-			pr_debug("usb: %s custom tune=0x%x\n",
-					__func__, phytune);
-			writel(phytune, EXYNOS5_PHY_OTG_TUNE);
-		}
-		phytune = readl(EXYNOS5_PHY_OTG_TUNE);
-		pr_debug("usb: %s modified tune=0x%x\n",
-				__func__, phytune);
-	} else {
-		pr_debug("usb: %s default tune\n", __func__);
-	}
-
-	return 0;
-}
-
-static void set_exynos_usb_phy_tune(int type)
-{
-	u32 phytune;
-
-	if (!soc_is_exynos5250()) {
-		pr_debug("usb: %s it is not exynos5250.(t=%d)\n",
-						__func__, type);
-		return;
-	}
-
-	if (type == S5P_USB_PHY_DEVICE) {
-		phytune = readl(EXYNOS5_PHY_OTG_TUNE);
-		pr_debug("usb: %s old phy tune for device =0x%x\n",
-					__func__, phytune);
-		/* sqrxtune [13:11] 3b110 : -15% */
-		phytune &= ~OTG_TUNE_SQRXTUNE(0x7);
-		phytune |= OTG_TUNE_SQRXTUNE(0x6);
-		udelay(10);
-		writel(phytune, EXYNOS5_PHY_OTG_TUNE);
-		phytune = readl(EXYNOS5_PHY_OTG_TUNE);
-		pr_debug("usb: %s new phy tune for device =0x%x\n",
-					__func__, phytune);
-	} else if (type == S5P_USB_PHY_HOST) {
-		phytune = readl(EXYNOS5_PHY_HOST_TUNE0);
-		pr_debug("usb: %s old phy tune for host =0x%x\n",
-				__func__, phytune);
-		/* sqrxtune [14:12] 3b110 : -15% */
-		phytune &= ~HOST_TUNE0_SQRXTUNE(0x7);
-		phytune |= HOST_TUNE0_SQRXTUNE(0x6);
-		udelay(10);
-		writel(phytune, EXYNOS5_PHY_HOST_TUNE0);
-		phytune = readl(EXYNOS5_PHY_HOST_TUNE0);
-		pr_debug("usb: %s new phy tune for host =0x%x\n",
-					__func__, phytune);
-	}
-}
-
 int s5p_usb_phy_init(struct platform_device *pdev, int type)
 {
 	int ret = -EINVAL;
 
 	if (type == S5P_USB_PHY_HOST) {
-		if (soc_is_exynos5250()) {
-			ret = exynos5_usb_phy20_init(pdev);
-			set_exynos_usb_phy_tune(type);
-		} else {
+		if (soc_is_exynos5250())
+			ret = exynos5_usb_host_phy20_init(pdev);
+		else
 			ret = exynos4_usb_phy1_init(pdev);
-		}
 	} else if (type == S5P_USB_PHY_DEVICE) {
 		ret = exynos_usb_dev_phy20_init(pdev);
-		set_exynos_usb_phy_tune(type);
-		/* set custom usb phy tune */
-		if (pdev->dev.platform_data)
-			ret = s5p_usb_otg_phy_tune(pdev->dev.platform_data, 0);
 	} else if (type == S5P_USB_PHY_DRD)
 		ret = exynos5_usb_phy30_init(pdev);
 
@@ -594,13 +638,10 @@
 
 	if (type == S5P_USB_PHY_HOST) {
 		if (soc_is_exynos5250())
-			ret = exynos5_usb_phy20_exit(pdev);
+			ret = exynos5_usb_host_phy20_exit(pdev);
 		else
 			ret = exynos4_usb_phy1_exit(pdev);
 	} else if (type == S5P_USB_PHY_DEVICE) {
-		/* set custom usb phy tune */
-		if (pdev->dev.platform_data)
-			ret = s5p_usb_otg_phy_tune(pdev->dev.platform_data, 1);
 		ret = exynos_usb_dev_phy20_exit(pdev);
 	} else if (type == S5P_USB_PHY_DRD) {
 		ret = exynos5_usb_phy30_exit(pdev);
diff --git a/arch/arm/mm/context.c b/arch/arm/mm/context.c
index 806cc4f..2ac3737 100644
--- a/arch/arm/mm/context.c
+++ b/arch/arm/mm/context.c
@@ -2,6 +2,9 @@
  *  linux/arch/arm/mm/context.c
  *
  *  Copyright (C) 2002-2003 Deep Blue Solutions Ltd, all rights reserved.
+ *  Copyright (C) 2012 ARM Limited
+ *
+ *  Author: Will Deacon <will.deacon@arm.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
@@ -14,13 +17,43 @@
 #include <linux/percpu.h>
 
 #include <asm/mmu_context.h>
+#include <asm/smp_plat.h>
+#include <asm/thread_notify.h>
 #include <asm/tlbflush.h>
 
+/*
+ * On ARMv6, we have the following structure in the Context ID:
+ *
+ * 31                         7          0
+ * +-------------------------+-----------+
+ * |      process ID         |   ASID    |
+ * +-------------------------+-----------+
+ * |              context ID             |
+ * +-------------------------------------+
+ *
+ * The ASID is used to tag entries in the CPU caches and TLBs.
+ * The context ID is used by debuggers and trace logic, and
+ * should be unique within all running processes.
+ *
+ * In big endian operation, the two 32 bit words are swapped if accesed by
+ * non 64-bit operations.
+ */
+#define ASID_FIRST_VERSION	(1ULL << ASID_BITS)
+#define NUM_USER_ASIDS		(ASID_FIRST_VERSION - 1)
+
+#define ASID_TO_IDX(asid)	((asid & ~ASID_MASK) - 1)
+#define IDX_TO_ASID(idx)	((idx + 1) & ~ASID_MASK)
+
 static DEFINE_RAW_SPINLOCK(cpu_asid_lock);
-unsigned int cpu_last_asid = ASID_FIRST_VERSION;
+static atomic64_t asid_generation = ATOMIC64_INIT(ASID_FIRST_VERSION);
+static DECLARE_BITMAP(asid_map, NUM_USER_ASIDS);
+
+DEFINE_PER_CPU(atomic64_t, active_asids);
+static DEFINE_PER_CPU(u64, reserved_asids);
+static cpumask_t tlb_flush_pending;
 
 #ifdef CONFIG_ARM_LPAE
-void cpu_set_reserved_ttbr0(void)
+static void cpu_set_reserved_ttbr0(void)
 {
 	unsigned long ttbl = __pa(swapper_pg_dir);
 	unsigned long ttbh = 0;
@@ -36,7 +69,7 @@
 	isb();
 }
 #else
-void cpu_set_reserved_ttbr0(void)
+static void cpu_set_reserved_ttbr0(void)
 {
 	u32 ttb;
 	/* Copy TTBR1 into TTBR0 */
@@ -48,124 +81,147 @@
 }
 #endif
 
-/*
- * We fork()ed a process, and we need a new context for the child
- * to run in.
- */
-void __init_new_context(struct task_struct *tsk, struct mm_struct *mm)
+#ifdef CONFIG_PID_IN_CONTEXTIDR
+static int contextidr_notifier(struct notifier_block *unused, unsigned long cmd,
+			       void *t)
 {
-	mm->context.id = 0;
-	raw_spin_lock_init(&mm->context.id_lock);
+	u32 contextidr;
+	pid_t pid;
+	struct thread_info *thread = t;
+
+	if (cmd != THREAD_NOTIFY_SWITCH)
+		return NOTIFY_DONE;
+
+	pid = task_pid_nr(thread->task) << ASID_BITS;
+	asm volatile(
+	"	mrc	p15, 0, %0, c13, c0, 1\n"
+	"	and	%0, %0, %2\n"
+	"	orr	%0, %0, %1\n"
+	"	mcr	p15, 0, %0, c13, c0, 1\n"
+	: "=r" (contextidr), "+r" (pid)
+	: "I" (~ASID_MASK));
+	isb();
+
+	return NOTIFY_OK;
 }
 
-static void flush_context(void)
+static struct notifier_block contextidr_notifier_block = {
+	.notifier_call = contextidr_notifier,
+};
+
+static int __init contextidr_notifier_init(void)
 {
-	cpu_set_reserved_ttbr0();
-	local_flush_tlb_all();
-	if (icache_is_vivt_asid_tagged()) {
-		__flush_icache_all();
-		dsb();
+	return thread_register_notifier(&contextidr_notifier_block);
+}
+arch_initcall(contextidr_notifier_init);
+#endif
+
+static void flush_context(unsigned int cpu)
+{
+	int i;
+	u64 asid;
+
+	/* Update the list of reserved ASIDs and the ASID bitmap. */
+	bitmap_clear(asid_map, 0, NUM_USER_ASIDS);
+	for_each_possible_cpu(i) {
+		if (i == cpu) {
+			asid = 0;
+		} else {
+			asid = atomic64_xchg(&per_cpu(active_asids, i), 0);
+			__set_bit(ASID_TO_IDX(asid), asid_map);
+		}
+		per_cpu(reserved_asids, i) = asid;
 	}
+
+	/* Queue a TLB invalidate and flush the I-cache if necessary. */
+	if (!tlb_ops_need_broadcast())
+		cpumask_set_cpu(cpu, &tlb_flush_pending);
+	else
+		cpumask_setall(&tlb_flush_pending);
+
+	if (icache_is_vivt_asid_tagged())
+		__flush_icache_all();
 }
 
-#ifdef CONFIG_SMP
-
-static void set_mm_context(struct mm_struct *mm, unsigned int asid)
+static int is_reserved_asid(u64 asid)
 {
-	unsigned long flags;
+	int cpu;
+	for_each_possible_cpu(cpu)
+		if (per_cpu(reserved_asids, cpu) == asid)
+			return 1;
+	return 0;
+}
 
-	/*
-	 * Locking needed for multi-threaded applications where the
-	 * same mm->context.id could be set from different CPUs during
-	 * the broadcast. This function is also called via IPI so the
-	 * mm->context.id_lock has to be IRQ-safe.
-	 */
-	raw_spin_lock_irqsave(&mm->context.id_lock, flags);
-	if (likely((mm->context.id ^ cpu_last_asid) >> ASID_BITS)) {
+static u64 new_context(struct mm_struct *mm, unsigned int cpu)
+{
+	u64 asid = atomic64_read(&mm->context.id);
+	u64 generation = atomic64_read(&asid_generation);
+
+	if (asid != 0 && is_reserved_asid(asid)) {
 		/*
-		 * Old version of ASID found. Set the new one and
-		 * reset mm_cpumask(mm).
+		 * Our current ASID was active during a rollover, we can
+		 * continue to use it and this was just a false alarm.
 		 */
-		mm->context.id = asid;
+		asid = generation | (asid & ~ASID_MASK);
+	} else {
+		/*
+		 * Allocate a free ASID. If we can't find one, take a
+		 * note of the currently active ASIDs and mark the TLBs
+		 * as requiring flushes.
+		 */
+		asid = find_first_zero_bit(asid_map, NUM_USER_ASIDS);
+		if (asid == NUM_USER_ASIDS) {
+			generation = atomic64_add_return(ASID_FIRST_VERSION,
+							 &asid_generation);
+			flush_context(cpu);
+			asid = find_first_zero_bit(asid_map, NUM_USER_ASIDS);
+		}
+		__set_bit(asid, asid_map);
+		asid = generation | IDX_TO_ASID(asid);
 		cpumask_clear(mm_cpumask(mm));
 	}
-	raw_spin_unlock_irqrestore(&mm->context.id_lock, flags);
 
-	/*
-	 * Set the mm_cpumask(mm) bit for the current CPU.
-	 */
-	cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));
+	return asid;
 }
 
-/*
- * Reset the ASID on the current CPU. This function call is broadcast
- * from the CPU handling the ASID rollover and holding cpu_asid_lock.
- */
-static void reset_context(void *info)
+void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk)
 {
-	unsigned int asid;
+	unsigned long flags;
 	unsigned int cpu = smp_processor_id();
-	struct mm_struct *mm = current->active_mm;
+	u64 asid;
 
-	smp_rmb();
-	asid = cpu_last_asid + cpu + 1;
+	if (unlikely(mm->context.vmalloc_seq != init_mm.context.vmalloc_seq))
+		__check_vmalloc_seq(mm);
 
-	flush_context();
-	set_mm_context(mm, asid);
+	/*
+	 * Required during context switch to avoid speculative page table
+	 * walking with the wrong TTBR.
+	 */
+	cpu_set_reserved_ttbr0();
 
-	/* set the new ASID */
+	asid = atomic64_read(&mm->context.id);
+	if (!((asid ^ atomic64_read(&asid_generation)) >> ASID_BITS)
+	    && atomic64_xchg(&per_cpu(active_asids, cpu), asid))
+		goto switch_mm_fastpath;
+
+	raw_spin_lock_irqsave(&cpu_asid_lock, flags);
+	/* Check that our ASID belongs to the current generation. */
+	asid = atomic64_read(&mm->context.id);
+	if ((asid ^ atomic64_read(&asid_generation)) >> ASID_BITS) {
+		asid = new_context(mm, cpu);
+		atomic64_set(&mm->context.id, asid);
+	}
+
+	if (cpumask_test_and_clear_cpu(cpu, &tlb_flush_pending)) {
+		local_flush_bp_all();
+		local_flush_tlb_all();
+		dummy_flush_tlb_a15_erratum();
+	}
+
+	atomic64_set(&per_cpu(active_asids, cpu), asid);
+	cpumask_set_cpu(cpu, mm_cpumask(mm));
+	raw_spin_unlock_irqrestore(&cpu_asid_lock, flags);
+
+switch_mm_fastpath:
 	cpu_switch_mm(mm->pgd, mm);
 }
-
-#else
-
-static inline void set_mm_context(struct mm_struct *mm, unsigned int asid)
-{
-	mm->context.id = asid;
-	cpumask_copy(mm_cpumask(mm), cpumask_of(smp_processor_id()));
-}
-
-#endif
-
-void __new_context(struct mm_struct *mm)
-{
-	unsigned int asid;
-
-	raw_spin_lock(&cpu_asid_lock);
-#ifdef CONFIG_SMP
-	/*
-	 * Check the ASID again, in case the change was broadcast from
-	 * another CPU before we acquired the lock.
-	 */
-	if (unlikely(((mm->context.id ^ cpu_last_asid) >> ASID_BITS) == 0)) {
-		cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));
-		raw_spin_unlock(&cpu_asid_lock);
-		return;
-	}
-#endif
-	/*
-	 * At this point, it is guaranteed that the current mm (with
-	 * an old ASID) isn't active on any other CPU since the ASIDs
-	 * are changed simultaneously via IPI.
-	 */
-	asid = ++cpu_last_asid;
-	if (asid == 0)
-		asid = cpu_last_asid = ASID_FIRST_VERSION;
-
-	/*
-	 * If we've used up all our ASIDs, we need
-	 * to start a new version and flush the TLB.
-	 */
-	if (unlikely((asid & ~ASID_MASK) == 0)) {
-		asid = cpu_last_asid + smp_processor_id() + 1;
-		flush_context();
-#ifdef CONFIG_SMP
-		smp_wmb();
-		smp_call_function(reset_context, NULL, 1);
-#endif
-		cpu_last_asid += NR_CPUS;
-	}
-
-	set_mm_context(mm, asid);
-	raw_spin_unlock(&cpu_asid_lock);
-}
diff --git a/arch/arm/mm/idmap.c b/arch/arm/mm/idmap.c
index ab88ed4..a249dfb 100644
--- a/arch/arm/mm/idmap.c
+++ b/arch/arm/mm/idmap.c
@@ -108,6 +108,7 @@
 
 	/* Switch to the identity mapping. */
 	cpu_switch_mm(idmap_pgd, &init_mm);
+	local_flush_bp_all();
 
 	/* Flush the TLB. */
 	local_flush_tlb_all();
diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c
index 4f55f50..135ebf1 100644
--- a/arch/arm/mm/ioremap.c
+++ b/arch/arm/mm/ioremap.c
@@ -46,18 +46,18 @@
 }
 EXPORT_SYMBOL(ioremap_page);
 
-void __check_kvm_seq(struct mm_struct *mm)
+void __check_vmalloc_seq(struct mm_struct *mm)
 {
 	unsigned int seq;
 
 	do {
-		seq = init_mm.context.kvm_seq;
+		seq = init_mm.context.vmalloc_seq;
 		memcpy(pgd_offset(mm, VMALLOC_START),
 		       pgd_offset_k(VMALLOC_START),
 		       sizeof(pgd_t) * (pgd_index(VMALLOC_END) -
 					pgd_index(VMALLOC_START)));
-		mm->context.kvm_seq = seq;
-	} while (seq != init_mm.context.kvm_seq);
+		mm->context.vmalloc_seq = seq;
+	} while (seq != init_mm.context.vmalloc_seq);
 }
 
 #if !defined(CONFIG_SMP) && !defined(CONFIG_ARM_LPAE)
@@ -88,13 +88,13 @@
 		if (!pmd_none(pmd)) {
 			/*
 			 * Clear the PMD from the page table, and
-			 * increment the kvm sequence so others
+			 * increment the vmalloc sequence so others
 			 * notice this change.
 			 *
 			 * Note: this is still racy on SMP machines.
 			 */
 			pmd_clear(pmdp);
-			init_mm.context.kvm_seq++;
+			init_mm.context.vmalloc_seq++;
 
 			/*
 			 * Free the page table, if there was one.
@@ -111,8 +111,8 @@
 	 * Ensure that the active_mm is up to date - we want to
 	 * catch any use-after-iounmap cases.
 	 */
-	if (current->active_mm->context.kvm_seq != init_mm.context.kvm_seq)
-		__check_kvm_seq(current->active_mm);
+	if (current->active_mm->context.vmalloc_seq != init_mm.context.vmalloc_seq)
+		__check_vmalloc_seq(current->active_mm);
 
 	flush_tlb_kernel_range(virt, end);
 }
diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S
index 2d8ff3a..74303e6 100644
--- a/arch/arm/mm/proc-macros.S
+++ b/arch/arm/mm/proc-macros.S
@@ -38,9 +38,14 @@
 
 /*
  * mmid - get context id from mm pointer (mm->context.id)
+ * note, this field is 64bit, so in big-endian the two words are swapped too.
  */
 	.macro	mmid, rd, rn
+#ifdef __ARMEB__
+	ldr	\rd, [\rn, #MM_CONTEXT_ID + 4 ]
+#else
 	ldr	\rd, [\rn, #MM_CONTEXT_ID]
+#endif
 	.endm
 
 /*
diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S
index 5900cd5..86b8b48 100644
--- a/arch/arm/mm/proc-v6.S
+++ b/arch/arm/mm/proc-v6.S
@@ -107,6 +107,12 @@
 	mcr	p15, 0, r2, c7, c5, 6		@ flush BTAC/BTB
 	mcr	p15, 0, r2, c7, c10, 4		@ drain write buffer
 	mcr	p15, 0, r0, c2, c0, 0		@ set TTB 0
+#ifdef CONFIG_PID_IN_CONTEXTIDR
+	mrc	p15, 0, r2, c13, c0, 1		@ read current context ID
+	bic	r2, r2, #0xff			@ extract the PID
+	and	r1, r1, #0xff
+	orr	r1, r1, r2			@ insert into new context ID
+#endif
 	mcr	p15, 0, r1, c13, c0, 1		@ set context ID
 #endif
 	mov	pc, lr
diff --git a/arch/arm/mm/proc-v7-2level.S b/arch/arm/mm/proc-v7-2level.S
index 05d8fe5..c663b2e 100644
--- a/arch/arm/mm/proc-v7-2level.S
+++ b/arch/arm/mm/proc-v7-2level.S
@@ -46,6 +46,11 @@
 #ifdef CONFIG_ARM_ERRATA_430973
 	mcr	p15, 0, r2, c7, c5, 6		@ flush BTAC/BTB
 #endif
+#ifdef CONFIG_PID_IN_CONTEXTIDR
+	mrc	p15, 0, r2, c13, c0, 1		@ read current context ID
+	lsr	r2, r2, #8			@ extract the PID
+	bfi	r1, r2, #8, #24			@ insert into new context ID
+#endif
 #ifdef CONFIG_ARM_ERRATA_754322
 	dsb
 #elif CONFIG_ARM_ERRATA_766421
diff --git a/arch/arm/plat-samsung/include/plat/regs-serial.h b/arch/arm/plat-samsung/include/plat/regs-serial.h
index 29c26a8..33ac5cf 100644
--- a/arch/arm/plat-samsung/include/plat/regs-serial.h
+++ b/arch/arm/plat-samsung/include/plat/regs-serial.h
@@ -246,6 +246,8 @@
 
 #ifndef __ASSEMBLY__
 
+struct uart_port;
+
 /* configuration structure for per-machine configurations for the
  * serial port
  *
@@ -265,6 +267,8 @@
 	unsigned long	   ucon;	 /* value of ucon for port */
 	unsigned long	   ulcon;	 /* value of ulcon for port */
 	unsigned long	   ufcon;	 /* value of ufcon for port */
+
+	void (*wake_peer)(struct uart_port *);
 };
 
 /* s3c24xx_uart_devs
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index f9c9f33..678972c 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -1169,3 +1169,4 @@
 pov2			MACH_POV2		POV2			3889
 ipod_touch_2g		MACH_IPOD_TOUCH_2G	IPOD_TOUCH_2G		3890
 da850_pqab		MACH_DA850_PQAB		DA850_PQAB		3891
+manta			MACH_MANTA		MANTA			3991
diff --git a/arch/microblaze/kernel/ptrace.c b/arch/microblaze/kernel/ptrace.c
index 6eb2aa9..ab1b9db 100644
--- a/arch/microblaze/kernel/ptrace.c
+++ b/arch/microblaze/kernel/ptrace.c
@@ -136,7 +136,7 @@
 {
 	long ret = 0;
 
-	secure_computing(regs->r12);
+	secure_computing_strict(regs->r12);
 
 	if (test_thread_flag(TIF_SYSCALL_TRACE) &&
 	    tracehook_report_syscall_entry(regs))
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index 7c24c29..4812c6d 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -535,7 +535,7 @@
 asmlinkage void syscall_trace_enter(struct pt_regs *regs)
 {
 	/* do the secure computing check first */
-	secure_computing(regs->regs[2]);
+	secure_computing_strict(regs->regs[2]);
 
 	if (!(current->ptrace & PT_PTRACED))
 		goto out;
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 8d8e028..dd5e214 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -1710,7 +1710,7 @@
 {
 	long ret = 0;
 
-	secure_computing(regs->gpr[0]);
+	secure_computing_strict(regs->gpr[0]);
 
 	if (test_thread_flag(TIF_SYSCALL_TRACE) &&
 	    tracehook_report_syscall_entry(regs))
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index 02f300f..4993e68 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -719,7 +719,7 @@
 	long ret = 0;
 
 	/* Do the secure computing check first. */
-	secure_computing(regs->gprs[2]);
+	secure_computing_strict(regs->gprs[2]);
 
 	/*
 	 * The sysc_tracesys code in entry.S stored the system
diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c
index 9698671..81f999a 100644
--- a/arch/sh/kernel/ptrace_32.c
+++ b/arch/sh/kernel/ptrace_32.c
@@ -503,7 +503,7 @@
 {
 	long ret = 0;
 
-	secure_computing(regs->regs[0]);
+	secure_computing_strict(regs->regs[0]);
 
 	if (test_thread_flag(TIF_SYSCALL_TRACE) &&
 	    tracehook_report_syscall_entry(regs))
diff --git a/arch/sh/kernel/ptrace_64.c b/arch/sh/kernel/ptrace_64.c
index bc81e07..af90339 100644
--- a/arch/sh/kernel/ptrace_64.c
+++ b/arch/sh/kernel/ptrace_64.c
@@ -522,7 +522,7 @@
 {
 	long long ret = 0;
 
-	secure_computing(regs->regs[9]);
+	secure_computing_strict(regs->regs[9]);
 
 	if (test_thread_flag(TIF_SYSCALL_TRACE) &&
 	    tracehook_report_syscall_entry(regs))
diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c
index 6f97c07..484daba 100644
--- a/arch/sparc/kernel/ptrace_64.c
+++ b/arch/sparc/kernel/ptrace_64.c
@@ -1062,7 +1062,7 @@
 	int ret = 0;
 
 	/* do the secure computing check first */
-	secure_computing(regs->u_regs[UREG_G1]);
+	secure_computing_strict(regs->u_regs[UREG_G1]);
 
 	if (test_thread_flag(TIF_SYSCALL_TRACE))
 		ret = tracehook_report_syscall_entry(regs);
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index b1478f4..357953f 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -81,7 +81,8 @@
 	select CLKEVT_I8253
 	select ARCH_HAVE_NMI_SAFE_CMPXCHG
 	select GENERIC_IOMAP
-	select DCACHE_WORD_ACCESS
+	select DCACHE_WORD_ACCESS if !DEBUG_PAGEALLOC
+	select HAVE_ARCH_SECCOMP_FILTER
 
 config INSTRUCTION_DECODER
 	def_bool (KPROBES || PERF_EVENTS)
diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c
index 4f5bfac..b154661 100644
--- a/arch/x86/ia32/ia32_signal.c
+++ b/arch/x86/ia32/ia32_signal.c
@@ -67,6 +67,10 @@
 			switch (from->si_code >> 16) {
 			case __SI_FAULT >> 16:
 				break;
+			case __SI_SYS >> 16:
+				put_user_ex(from->si_syscall, &to->si_syscall);
+				put_user_ex(from->si_arch, &to->si_arch);
+				break;
 			case __SI_CHLD >> 16:
 				if (ia32) {
 					put_user_ex(from->si_utime, &to->si_utime);
diff --git a/arch/x86/include/asm/ia32.h b/arch/x86/include/asm/ia32.h
index ee52760..b04cbdb 100644
--- a/arch/x86/include/asm/ia32.h
+++ b/arch/x86/include/asm/ia32.h
@@ -144,6 +144,12 @@
 			int _band;	/* POLL_IN, POLL_OUT, POLL_MSG */
 			int _fd;
 		} _sigpoll;
+
+		struct {
+			unsigned int _call_addr; /* calling insn */
+			int _syscall;	/* triggering system call number */
+			unsigned int _arch;	/* AUDIT_ARCH_* of syscall */
+		} _sigsys;
 	} _sifields;
 } compat_siginfo_t;
 
diff --git a/arch/x86/include/asm/syscall.h b/arch/x86/include/asm/syscall.h
index 386b786..1ace47b 100644
--- a/arch/x86/include/asm/syscall.h
+++ b/arch/x86/include/asm/syscall.h
@@ -13,9 +13,11 @@
 #ifndef _ASM_X86_SYSCALL_H
 #define _ASM_X86_SYSCALL_H
 
+#include <linux/audit.h>
 #include <linux/sched.h>
 #include <linux/err.h>
 #include <asm/asm-offsets.h>	/* For NR_syscalls */
+#include <asm/thread_info.h>	/* for TS_COMPAT */
 #include <asm/unistd.h>
 
 extern const unsigned long sys_call_table[];
@@ -88,6 +90,12 @@
 	memcpy(&regs->bx + i, args, n * sizeof(args[0]));
 }
 
+static inline int syscall_get_arch(struct task_struct *task,
+				   struct pt_regs *regs)
+{
+	return AUDIT_ARCH_I386;
+}
+
 #else	 /* CONFIG_X86_64 */
 
 static inline void syscall_get_arguments(struct task_struct *task,
@@ -212,6 +220,25 @@
 		}
 }
 
+static inline int syscall_get_arch(struct task_struct *task,
+				   struct pt_regs *regs)
+{
+#ifdef CONFIG_IA32_EMULATION
+	/*
+	 * TS_COMPAT is set for 32-bit syscall entry and then
+	 * remains set until we return to user mode.
+	 *
+	 * TIF_IA32 tasks should always have TS_COMPAT set at
+	 * system call time.
+	 *
+	 * x32 tasks should be considered AUDIT_ARCH_X86_64.
+	 */
+	if (task_thread_info(task)->status & TS_COMPAT)
+		return AUDIT_ARCH_I386;
+#endif
+	/* Both x32 and x86_64 are considered "64-bit". */
+	return AUDIT_ARCH_X86_64;
+}
 #endif	/* CONFIG_X86_32 */
 
 #endif	/* _ASM_X86_SYSCALL_H */
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index c4410fb..9ee1787 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -1504,7 +1504,11 @@
 		regs->flags |= X86_EFLAGS_TF;
 
 	/* do the secure computing check first */
-	secure_computing(regs->orig_ax);
+	if (secure_computing(regs->orig_ax)) {
+		/* seccomp failures shouldn't expose any additional code. */
+		ret = -1L;
+		goto out;
+	}
 
 	if (unlikely(test_thread_flag(TIF_SYSCALL_EMU)))
 		ret = -1L;
@@ -1529,6 +1533,7 @@
 				    regs->dx, regs->r10);
 #endif
 
+out:
 	return ret ?: regs->orig_ax;
 }
 
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 021a79c..22e7f12 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -142,6 +142,8 @@
 
 source "drivers/devfreq/Kconfig"
 
+source "drivers/nfc/Kconfig"
+
 source "drivers/gud/Kconfig"
 
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 0ddf519..2f13629 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -125,7 +125,7 @@
 obj-y				+= clk/
 
 obj-$(CONFIG_HWSPINLOCK)	+= hwspinlock/
-obj-$(CONFIG_NFC)		+= nfc/
+obj-$(CONFIG_NFC_DEVICES)		+= nfc/
 obj-$(CONFIG_IOMMU_SUPPORT)	+= iommu/
 obj-$(CONFIG_REMOTEPROC)	+= remoteproc/
 obj-$(CONFIG_RPMSG)		+= rpmsg/
diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c
index aa61ad2..fa8620c 100644
--- a/drivers/gpio/gpio-wm8994.c
+++ b/drivers/gpio/gpio-wm8994.c
@@ -254,7 +254,8 @@
 	struct wm8994_gpio *wm8994_gpio;
 	int ret;
 
-	wm8994_gpio = kzalloc(sizeof(*wm8994_gpio), GFP_KERNEL);
+	wm8994_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm8994_gpio),
+				   GFP_KERNEL);
 	if (wm8994_gpio == NULL)
 		return -ENOMEM;
 
@@ -279,20 +280,14 @@
 	return ret;
 
 err:
-	kfree(wm8994_gpio);
 	return ret;
 }
 
 static int __devexit wm8994_gpio_remove(struct platform_device *pdev)
 {
 	struct wm8994_gpio *wm8994_gpio = platform_get_drvdata(pdev);
-	int ret;
 
-	ret = gpiochip_remove(&wm8994_gpio->gpio_chip);
-	if (ret == 0)
-		kfree(wm8994_gpio);
-
-	return ret;
+	return gpiochip_remove(&wm8994_gpio->gpio_chip);
 }
 
 static struct platform_driver wm8994_gpio_driver = {
diff --git a/drivers/gpu/ion/Kconfig b/drivers/gpu/ion/Kconfig
index 442f697..4b78adf 100644
--- a/drivers/gpu/ion/Kconfig
+++ b/drivers/gpu/ion/Kconfig
@@ -46,7 +46,7 @@
 config ION_EXYNOS_DRM_MEMSIZE_FIMD_VIDEO
 	int "Reserved memsize in kilobytes for FIMD VIDEO"
 	depends on EXYNOS_CONTENT_PATH_PROTECTION
-	default 98304
+	default 184320
 
 config ION_EXYNOS_DRM_MEMSIZE_MFC_OUTPUT
 	int "Reserved memsize in kilobytes for MFC OUTPUT"
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 8921c61..aeccc69 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -515,11 +515,14 @@
 
 	handle->open++;
 
-	if (!dev->users++ && dev->open)
+	dev->users_private++;
+	if (!dev->disabled && !dev->users++ && dev->open)
 		retval = dev->open(dev);
 
 	if (retval) {
-		dev->users--;
+		dev->users_private--;
+		if (!dev->disabled)
+			dev->users--;
 		if (!--handle->open) {
 			/*
 			 * Make sure we are not delivering any more events
@@ -567,7 +570,8 @@
 
 	__input_release_device(handle);
 
-	if (!--dev->users && dev->close)
+	--dev->users_private;
+	if (!dev->disabled && !--dev->users && dev->close)
 		dev->close(dev);
 
 	if (!--handle->open) {
@@ -583,6 +587,50 @@
 }
 EXPORT_SYMBOL(input_close_device);
 
+static int input_enable_device(struct input_dev *dev)
+{
+	int retval;
+
+	retval = mutex_lock_interruptible(&dev->mutex);
+	if (retval)
+		return retval;
+
+	if (!dev->disabled)
+		goto out;
+
+	if (dev->users_private && dev->open) {
+		retval = dev->open(dev);
+		if (retval)
+			goto out;
+	}
+	dev->users = dev->users_private;
+	dev->disabled = false;
+
+out:
+	mutex_unlock(&dev->mutex);
+
+	return retval;
+}
+
+static int input_disable_device(struct input_dev *dev)
+{
+	int retval;
+
+	retval = mutex_lock_interruptible(&dev->mutex);
+	if (retval)
+		return retval;
+
+	if (!dev->disabled) {
+		dev->disabled = true;
+		if (dev->users && dev->close)
+			dev->close(dev);
+		dev->users = 0;
+	}
+
+	mutex_unlock(&dev->mutex);
+	return 0;
+}
+
 /*
  * Simulate keyup events for all keys that are marked as pressed.
  * The function must be called with dev->event_lock held.
@@ -1291,12 +1339,46 @@
 }
 static DEVICE_ATTR(properties, S_IRUGO, input_dev_show_properties, NULL);
 
+static ssize_t input_dev_show_enabled(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct input_dev *input_dev = to_input_dev(dev);
+	return scnprintf(buf, PAGE_SIZE, "%d\n", !input_dev->disabled);
+}
+
+static ssize_t input_dev_store_enabled(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t size)
+{
+	int ret;
+	bool enable;
+	struct input_dev *input_dev = to_input_dev(dev);
+
+	ret = strtobool(buf, &enable);
+	if (ret)
+		return ret;
+
+	if (enable)
+		ret = input_enable_device(input_dev);
+	else
+		ret = input_disable_device(input_dev);
+	if (ret)
+		return ret;
+
+	return size;
+}
+
+static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR,
+		   input_dev_show_enabled, input_dev_store_enabled);
+
 static struct attribute *input_dev_attrs[] = {
 	&dev_attr_name.attr,
 	&dev_attr_phys.attr,
 	&dev_attr_uniq.attr,
 	&dev_attr_modalias.attr,
 	&dev_attr_properties.attr,
+	&dev_attr_enabled.attr,
 	NULL
 };
 
diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c
index 36208fe..5b6c73b 100644
--- a/drivers/input/keyreset.c
+++ b/drivers/input/keyreset.c
@@ -21,6 +21,7 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/syscalls.h>
+#include <linux/workqueue.h>
 
 
 struct keyreset_state {
@@ -33,18 +34,28 @@
 	int key_down;
 	int key_up;
 	int restart_disabled;
+	int restart_requested;
 	int (*reset_fn)(void);
+	int down_time_ms;
+	struct delayed_work restart_work;
 };
 
-int restart_requested;
-static void deferred_restart(struct work_struct *dummy)
+static void deferred_restart(struct work_struct *work)
 {
-	restart_requested = 2;
-	sys_sync();
-	restart_requested = 3;
-	kernel_restart(NULL);
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct keyreset_state *state =
+		container_of(dwork, struct keyreset_state, restart_work);
+
+	pr_info("keyreset: restarting system\n");
+	if (state->reset_fn) {
+		state->restart_requested = state->reset_fn();
+	} else {
+		state->restart_requested = 2;
+		sys_sync();
+		state->restart_requested = 3;
+		kernel_restart(NULL);
+	}
 }
-static DECLARE_WORK(restart_work, deferred_restart);
 
 static void keyreset_event(struct input_handle *handle, unsigned int type,
 			   unsigned int code, int value)
@@ -77,8 +88,16 @@
 		else
 			state->key_down--;
 	}
-	if (state->key_down == 0 && state->key_up == 0)
+	if (state->key_down == 0 && state->key_up == 0) {
 		state->restart_disabled = 0;
+		if (state->down_time_ms) {
+			__cancel_delayed_work(&state->restart_work);
+			if (state->restart_requested) {
+				pr_info("keyboard reset canceled\n");
+				state->restart_requested = 0;
+			}
+		}
+	}
 
 	pr_debug("reset key changed %d %d new state %d-%d-%d\n", code, value,
 		 state->key_down, state->key_up, state->restart_disabled);
@@ -86,14 +105,17 @@
 	if (value && !state->restart_disabled &&
 	    state->key_down == state->key_down_target) {
 		state->restart_disabled = 1;
-		if (restart_requested)
-			panic("keyboard reset failed, %d", restart_requested);
-		if (state->reset_fn) {
-			restart_requested = state->reset_fn();
+		if (state->restart_requested)
+			panic("keyboard reset failed, %d",
+			      state->restart_requested);
+		if (state->reset_fn && state->down_time_ms == 0) {
+			state->restart_requested = state->reset_fn();
 		} else {
-			pr_info("keyboard reset\n");
-			schedule_work(&restart_work);
-			restart_requested = 1;
+			pr_info("keyboard reset (delayed %dms)\n",
+				state->down_time_ms);
+			schedule_delayed_work(&state->restart_work,
+				msecs_to_jiffies(state->down_time_ms));
+			state->restart_requested = 1;
 		}
 	}
 done:
@@ -136,6 +158,13 @@
 
 	pr_info("using input dev %s for key reset\n", dev->name);
 
+	/* process already pressed keys */
+	for_each_set_bit(i, state->keybit, KEY_CNT) {
+		if (!test_bit(i, dev->keybit) || !test_bit(i, dev->key))
+			continue;
+		keyreset_event(handle, EV_KEY, i, 1);
+	}
+
 	return 0;
 
 err_input_open_device:
@@ -196,6 +225,11 @@
 	if (pdata->reset_fn)
 		state->reset_fn = pdata->reset_fn;
 
+	if (pdata->down_time_ms)
+		state->down_time_ms = pdata->down_time_ms;
+
+	INIT_DELAYED_WORK(&state->restart_work, deferred_restart);
+
 	state->input_handler.event = keyreset_event;
 	state->input_handler.connect = keyreset_connect;
 	state->input_handler.disconnect = keyreset_disconnect;
@@ -214,6 +248,7 @@
 {
 	struct keyreset_state *state = platform_get_drvdata(pdev);
 	input_unregister_handler(&state->input_handler);
+	cancel_delayed_work_sync(&state->restart_work);
 	kfree(state);
 	return 0;
 }
@@ -235,5 +270,5 @@
 	return platform_driver_unregister(&keyreset_driver);
 }
 
-module_init(keyreset_init);
+subsys_initcall(keyreset_init);
 module_exit(keyreset_exit);
diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c
index c7f396a..ceb1d2f 100644
--- a/drivers/input/misc/gpio_event.c
+++ b/drivers/input/misc/gpio_event.c
@@ -231,7 +231,7 @@
 	platform_driver_unregister(&gpio_event_driver);
 }
 
-module_init(gpio_event_init);
+subsys_initcall(gpio_event_init);
 module_exit(gpio_event_exit);
 
 MODULE_DESCRIPTION("GPIO Event Driver");
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 19d4ea6..341fd7d 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -20,6 +20,8 @@
 #include <linux/input/mt.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
 
 /* Version */
 #define MXT_VER_20		20
@@ -75,6 +77,12 @@
 #define MXT_SPT_DIGITIZER_T43		43
 #define MXT_SPT_MESSAGECOUNT_T44	44
 #define MXT_SPT_CTECONFIG_T46		46
+#define MXT_ADAPTIVE_T55		55
+#define MXT_PROCI_SHIELDLESS_T56	56
+#define MXT_PROCI_EXTRATOUCHSCREENDATA_T57	57
+#define MXT_SPT_TIMER_T61			61
+#define MXT_PROCG_NOISESUPPRESSION_T62		62
+#define MXT_PROCI_ACTIVESTYLUS_63		63
 
 /* MXT_GEN_COMMAND_T6 field */
 #define MXT_COMMAND_RESET	0
@@ -175,8 +183,10 @@
 /* Define for MXT_GEN_COMMAND_T6 */
 #define MXT_BOOT_VALUE		0xa5
 #define MXT_BACKUP_VALUE	0x55
+#define MXT_DISABLE_EVENT_VALUE	0x33
+
 #define MXT_BACKUP_TIME		25	/* msec */
-#define MXT_RESET_TIME		65	/* msec */
+#define MXT_RESET_TIME		300	/* msec */
 
 #define MXT_FWRESET_TIME	175	/* msec */
 
@@ -212,6 +222,17 @@
 
 #define MXT_MAX_FINGER		10
 
+/* Touchscreen configuration infomation */
+#define MXT_FW_MAGIC		0x4D3C2B1A
+
+/* Voltage supplies */
+static const char * const mxt_supply_names[] = {
+	/* keep the order for power sequence */
+	"vdd",
+	"avdd",
+	"xvdd",
+};
+
 struct mxt_info {
 	u8 family_id;
 	u8 variant_id;
@@ -245,11 +266,18 @@
 	int y;
 	int area;
 	int pressure;
+	int vector;
+};
+
+struct mxt_reportid {
+	u8 type;
+	u8 index;
 };
 
 /* Each client has this additional data */
 struct mxt_data {
 	struct i2c_client *client;
+	struct i2c_client *client_boot;
 	struct input_dev *input_dev;
 	const struct mxt_platform_data *pdata;
 	struct mxt_object *object_table;
@@ -258,6 +286,59 @@
 	unsigned int irq;
 	unsigned int max_x;
 	unsigned int max_y;
+	struct completion init_done;
+	unsigned int max_reportid;
+	struct mxt_reportid *reportid_table;
+	bool enabled;
+	struct regulator_bulk_data supplies[ARRAY_SIZE(mxt_supply_names)];
+};
+
+/**
+ * struct mxt_fw_image - Represents a firmware file.
+ * @magic_code: Identifier of file type.
+ * @hdr_len: Size of file header (struct mxt_fw_image).
+ * @cfg_len: Size of configuration data.
+ * @fw_len: Size of firmware data.
+ * @cfg_crc: CRC of configuration settings.
+ * @fw_ver: Version of firmware.
+ * @build_ver: Build version of firmware.
+ * @data: Configuration data followed by firmware image.
+ */
+struct mxt_fw_image {
+	__le32 magic_code;
+	__le32 hdr_len;
+	__le32 cfg_len;
+	__le32 fw_len;
+	__le32 cfg_crc;
+	u8 fw_ver;
+	u8 build_ver;
+	u8 data[0];
+} __packed;
+
+/**
+ * struct mxt_cfg_data - Represents a configuration data item.
+ * @type: Type of object.
+ * @instance: Instance number of object.
+ * @size: Size of object.
+ * @register_val: Series of register values for object.
+ */
+struct mxt_cfg_data {
+	u8 type;
+	u8 instance;
+	u8 size;
+	u8 register_val[0];
+} __packed;
+
+struct mxt_fw_info {
+	u8 fw_ver;
+	u8 build_ver;
+	u32 hdr_len;
+	u32 cfg_len;
+	u32 fw_len;
+	u32 cfg_crc;
+	const u8 *cfg_raw_data;	/* start address of configuration data */
+	const u8 *fw_raw_data;	/* start address of firmware data */
+	struct mxt_data *data;
 };
 
 static bool mxt_object_readable(unsigned int type)
@@ -288,6 +369,12 @@
 	case MXT_SPT_USERDATA_T38:
 	case MXT_SPT_DIGITIZER_T43:
 	case MXT_SPT_CTECONFIG_T46:
+	case MXT_ADAPTIVE_T55:
+	case MXT_PROCI_SHIELDLESS_T56:
+	case MXT_PROCI_EXTRATOUCHSCREENDATA_T57:
+	case MXT_SPT_TIMER_T61:
+	case MXT_PROCG_NOISESUPPRESSION_T62:
+	case MXT_PROCI_ACTIVESTYLUS_63:
 		return true;
 	default:
 		return false;
@@ -319,6 +406,12 @@
 	case MXT_SPT_CTECONFIG_T28:
 	case MXT_SPT_DIGITIZER_T43:
 	case MXT_SPT_CTECONFIG_T46:
+	case MXT_ADAPTIVE_T55:
+	case MXT_PROCI_SHIELDLESS_T56:
+	case MXT_PROCI_EXTRATOUCHSCREENDATA_T57:
+	case MXT_SPT_TIMER_T61:
+	case MXT_PROCG_NOISESUPPRESSION_T62:
+	case MXT_PROCI_ACTIVESTYLUS_63:
 		return true;
 	default:
 		return false;
@@ -326,17 +419,52 @@
 }
 
 static void mxt_dump_message(struct device *dev,
-				  struct mxt_message *message)
+				  struct mxt_message *message, u8 object_type)
 {
-	dev_dbg(dev, "reportid:\t0x%x\n", message->reportid);
-	dev_dbg(dev, "message1:\t0x%x\n", message->message[0]);
-	dev_dbg(dev, "message2:\t0x%x\n", message->message[1]);
-	dev_dbg(dev, "message3:\t0x%x\n", message->message[2]);
-	dev_dbg(dev, "message4:\t0x%x\n", message->message[3]);
-	dev_dbg(dev, "message5:\t0x%x\n", message->message[4]);
-	dev_dbg(dev, "message6:\t0x%x\n", message->message[5]);
-	dev_dbg(dev, "message7:\t0x%x\n", message->message[6]);
-	dev_dbg(dev, "checksum:\t0x%x\n", message->checksum);
+	switch (object_type) {
+	case MXT_GEN_COMMAND_T6:
+		/* Normal mode */
+		if (message->message[0] == 0x00)
+			dev_dbg(dev, "Normal mode\n");
+		/* Config error */
+		if (message->message[0] & 0x08)
+			dev_err(dev, "Configuration error\n");
+		/* Calibration */
+		if (message->message[0] & 0x10)
+			dev_dbg(dev, "Calibration is on going !!\n");
+		/* Signal error */
+		if (message->message[0] & 0x20)
+			dev_err(dev, "Signal error\n");
+		/* Overflow */
+		if (message->message[0] & 0x40)
+			dev_err(dev, "Overflow detected\n");
+		/* Reset */
+		if (message->message[0] & 0x80)
+			dev_dbg(dev, "Reset is ongoing\n");
+		break;
+
+	case MXT_SPT_SELFTEST_T25:
+		if (message->message[0] != 0xFE)
+			dev_err(dev, "Self-test error:[0x%x][0x%x][0x%x][0x%x]\n",
+			message->message[0], message->message[1],
+			message->message[2], message->message[3]);
+		break;
+
+	case MXT_PROCI_TOUCHSUPPRESSION_T42:
+		if (message->message[0] & 0x01)
+			dev_dbg(dev, "Start touch suppression\n");
+		else
+			dev_dbg(dev, "Stop touch suppression\n");
+		break;
+	default:
+		dev_dbg(dev, "T%d:\t[0x%x][0x%x][0x%x][0x%x][0x%x][0x%x][0x%x][0x%x][0x%x]\n",
+			object_type, message->reportid,
+			message->message[0], message->message[1],
+			message->message[2], message->message[3],
+			message->message[4], message->message[5],
+			message->message[6], message->checksum);
+		break;
+	}
 }
 
 static int mxt_check_bootloader(struct i2c_client *client,
@@ -353,18 +481,24 @@
 	switch (state) {
 	case MXT_WAITING_BOOTLOAD_CMD:
 	case MXT_WAITING_FRAME_DATA:
+	case MXT_APP_CRC_FAIL:
 		val &= ~MXT_BOOT_STATUS_MASK;
 		break;
 	case MXT_FRAME_CRC_PASS:
 		if (val == MXT_FRAME_CRC_CHECK)
 			goto recheck;
+		if (val == MXT_FRAME_CRC_FAIL) {
+			dev_err(&client->dev, "Bootloader CRC fail\n");
+			return -EINVAL;
+		}
 		break;
 	default:
 		return -EINVAL;
 	}
 
 	if (val != state) {
-		dev_err(&client->dev, "Unvalid bootloader mode state\n");
+		dev_err(&client->dev,
+			 "Invalid bootloader mode state 0x%X\n", val);
 		return -EINVAL;
 	}
 
@@ -386,6 +520,28 @@
 	return 0;
 }
 
+static int mxt_probe_bootloader(struct i2c_client *client)
+{
+	u8 val;
+
+	if (i2c_master_recv(client, &val, 1) != 1) {
+		dev_err(&client->dev, "%s: i2c recv failed\n", __func__);
+		return -EIO;
+	}
+
+	if (val & (~MXT_BOOT_STATUS_MASK)) {
+		if (val & MXT_APP_CRC_FAIL)
+			dev_err(&client->dev, "Application CRC failure\n");
+		else
+			dev_err(&client->dev, "Device in bootloader mode\n");
+	} else {
+		dev_err(&client->dev, "%s: Unknow status\n", __func__);
+		return -EIO;
+	}
+
+	return 0;
+}
+
 static int mxt_fw_write(struct i2c_client *client,
 			     const u8 *data, unsigned int frame_size)
 {
@@ -460,6 +616,9 @@
 	struct mxt_object *object;
 	int i;
 
+	if (!data->object_table)
+		return NULL;
+
 	for (i = 0; i < data->info.object_num; i++) {
 		object = data->object_table + i;
 		if (object->type == type)
@@ -485,6 +644,30 @@
 			sizeof(struct mxt_message), message);
 }
 
+static int mxt_read_message_reportid(struct mxt_data *data,
+	struct mxt_message *message, u8 reportid)
+{
+	int try = 0;
+	int error;
+	int fail_count;
+
+	fail_count = data->max_reportid * 2;
+
+	while (++try < fail_count) {
+		error = mxt_read_message(data, message);
+		if (error)
+			return error;
+
+		if (message->reportid == 0xff)
+			continue;
+
+		if (message->reportid == reportid)
+			return 0;
+	}
+
+	return -EINVAL;
+}
+
 static int mxt_read_object(struct mxt_data *data,
 				u8 type, u8 offset, u8 *val)
 {
@@ -513,11 +696,10 @@
 	return mxt_write_reg(data->client, reg + offset, val);
 }
 
-static void mxt_input_report(struct mxt_data *data, int single_id)
+static void mxt_input_report(struct mxt_data *data)
 {
 	struct mxt_finger *finger = data->finger;
 	struct input_dev *input_dev = data->input_dev;
-	int status = finger[single_id].status;
 	int finger_num = 0;
 	int id;
 
@@ -539,20 +721,13 @@
 					finger[id].y);
 			input_report_abs(input_dev, ABS_MT_PRESSURE,
 					finger[id].pressure);
+			input_report_abs(input_dev, ABS_MT_ORIENTATION,
+					finger[id].vector);
 		} else {
 			finger[id].status = 0;
 		}
 	}
 
-	input_report_key(input_dev, BTN_TOUCH, finger_num > 0);
-
-	if (status != MXT_RELEASE) {
-		input_report_abs(input_dev, ABS_X, finger[single_id].x);
-		input_report_abs(input_dev, ABS_Y, finger[single_id].y);
-		input_report_abs(input_dev,
-				 ABS_PRESSURE, finger[single_id].pressure);
-	}
-
 	input_sync(input_dev);
 }
 
@@ -566,6 +741,12 @@
 	int y;
 	int area;
 	int pressure;
+	int vector;
+
+	if (id >= MXT_MAX_FINGER) {
+		dev_err(dev, "MXT_MAX_FINGER exceeded!\n");
+		return;
+	}
 
 	/* Check the touch is present on the screen */
 	if (!(status & MXT_DETECT)) {
@@ -573,15 +754,16 @@
 			dev_dbg(dev, "[%d] released\n", id);
 
 			finger[id].status = MXT_RELEASE;
-			mxt_input_report(data, id);
+			mxt_input_report(data);
+		} else if (status & MXT_SUPPRESS) {
+			dev_dbg(dev, "[%d] suppressed\n", id);
+
+			finger[id].status = MXT_RELEASE;
+			mxt_input_report(data);
 		}
 		return;
 	}
 
-	/* Check only AMP detection */
-	if (!(status & (MXT_PRESS | MXT_MOVE)))
-		return;
-
 	x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf);
 	y = (message->message[2] << 4) | ((message->message[3] & 0xf));
 	if (data->max_x < 1024)
@@ -591,6 +773,7 @@
 
 	area = message->message[4];
 	pressure = message->message[5];
+	vector = message->message[6];
 
 	dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id,
 		status & MXT_MOVE ? "moved" : "pressed",
@@ -602,20 +785,19 @@
 	finger[id].y = y;
 	finger[id].area = area;
 	finger[id].pressure = pressure;
+	finger[id].vector = vector;
 
-	mxt_input_report(data, id);
+	mxt_input_report(data);
 }
 
 static irqreturn_t mxt_interrupt(int irq, void *dev_id)
 {
 	struct mxt_data *data = dev_id;
 	struct mxt_message message;
-	struct mxt_object *object;
 	struct device *dev = &data->client->dev;
 	int id;
 	u8 reportid;
-	u8 max_reportid;
-	u8 min_reportid;
+	u8 object_type = 0;
 
 	do {
 		if (mxt_read_message(data, &message)) {
@@ -625,25 +807,173 @@
 
 		reportid = message.reportid;
 
-		/* whether reportid is thing of MXT_TOUCH_MULTI_T9 */
-		object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
-		if (!object)
+		if (reportid > data->max_reportid)
 			goto end;
 
-		max_reportid = object->max_reportid;
-		min_reportid = max_reportid - object->num_report_ids + 1;
-		id = reportid - min_reportid;
+		object_type = data->reportid_table[reportid].type;
 
-		if (reportid >= min_reportid && reportid <= max_reportid)
+		if (object_type == MXT_TOUCH_MULTI_T9) {
+			id = data->reportid_table[reportid].index;
 			mxt_input_touchevent(data, &message, id);
-		else
-			mxt_dump_message(dev, &message);
+		} else {
+			mxt_dump_message(dev, &message, object_type);
+		}
 	} while (reportid != 0xff);
 
 end:
 	return IRQ_HANDLED;
 }
 
+static int mxt_read_config_crc(struct mxt_data *data, u32 *crc)
+{
+	struct device *dev = &data->client->dev;
+	int error;
+	struct mxt_message message;
+	struct mxt_object *object;
+
+	object = mxt_get_object(data, MXT_GEN_COMMAND_T6);
+	if (!object)
+		return -EIO;
+
+	/* Try to read the config checksum of the existing cfg */
+	mxt_write_object(data, MXT_GEN_COMMAND_T6,
+		MXT_COMMAND_REPORTALL, 1);
+
+	/* Read message from command processor, which only has one report ID */
+	error = mxt_read_message_reportid(data, &message, object->max_reportid);
+	if (error) {
+		dev_err(dev, "Failed to retrieve CRC\n");
+		return error;
+	}
+
+	/* Bytes 1-3 are the checksum. */
+	*crc = message.message[1] | (message.message[2] << 8) |
+		(message.message[3] << 16);
+
+	return 0;
+}
+
+static int mxt_download_config(struct mxt_fw_info *fw_info)
+{
+	struct mxt_data *data = fw_info->data;
+	struct device *dev = &data->client->dev;
+	struct mxt_object *object;
+	struct mxt_cfg_data *cfg_data;
+	u32 current_crc;
+	u8 i;
+	u16 reg, index;
+	int ret;
+
+	if (!fw_info->cfg_raw_data) {
+		dev_err(dev, "Configuration data is Null\n");
+		return -EINVAL;
+	}
+
+	/* Get config CRC from device */
+	ret = mxt_read_config_crc(data, &current_crc);
+	if (ret)
+		return ret;
+
+	/* Check Version information */
+	if (fw_info->fw_ver != data->info.version) {
+		dev_err(dev, "Warning: version mismatch! %s\n", __func__);
+		return 0;
+	}
+	if (fw_info->build_ver != data->info.build) {
+		dev_err(dev, "Warning: build num mismatch! %s\n", __func__);
+		return 0;
+	}
+
+	/* Check config CRC */
+	if (current_crc == fw_info->cfg_crc) {
+		dev_info(dev, "Skip writing Config:[CRC 0x%06X]\n",
+			current_crc);
+		return 0;
+	}
+
+	dev_info(dev, "Writing Config:[CRC 0x%06X!=0x%06X]\n",
+		current_crc, fw_info->cfg_crc);
+
+	/* Write config info */
+	for (index = 0; index < fw_info->cfg_len;) {
+
+		if (index + sizeof(struct mxt_cfg_data) >= fw_info->cfg_len) {
+			dev_err(dev, "index(%d) of cfg_data exceeded total size(%d)!!\n",
+				index + sizeof(struct mxt_cfg_data),
+				fw_info->cfg_len);
+			return -EINVAL;
+		}
+
+		/* Get the info about each object */
+		cfg_data = (struct mxt_cfg_data *)
+					(&fw_info->cfg_raw_data[index]);
+
+		index += sizeof(struct mxt_cfg_data) + cfg_data->size;
+		if (index > fw_info->cfg_len) {
+			dev_err(dev, "index(%d) of cfg_data exceeded total size(%d) in T%d object!!\n",
+				index, fw_info->cfg_len, cfg_data->type);
+			return -EINVAL;
+		}
+
+		object = mxt_get_object(data, cfg_data->type);
+		if (!object) {
+			dev_err(dev, "T%d is Invalid object type\n",
+				cfg_data->type);
+			return -EINVAL;
+		}
+
+		/* Check and compare the size, instance of each object */
+		if (cfg_data->size > object->size) {
+			dev_err(dev, "T%d Object length exceeded!\n",
+				cfg_data->type);
+			return -EINVAL;
+		}
+		if (cfg_data->instance >= object->instances) {
+			dev_err(dev, "T%d Object instances exceeded!\n",
+				cfg_data->type);
+			return -EINVAL;
+		}
+
+		dev_dbg(dev, "Writing config for obj %d len %d instance %d (%d/%d)\n",
+			cfg_data->type, object->size,
+			cfg_data->instance, index, fw_info->cfg_len);
+
+		reg = object->start_address + object->size * cfg_data->instance;
+
+		/* Write register values of each object */
+		for (i = 0; i < cfg_data->size; i++) {
+			ret = mxt_write_reg(data->client, reg + i,
+					cfg_data->register_val[i]);
+			if (ret) {
+				dev_err(dev, "Write %dth byte of T%d Object failed\n",
+					object->type, i);
+				return ret;
+			}
+		}
+
+		/*
+		 * If firmware is upgraded, new bytes may be added to end of
+		 * objects. It is generally forward compatible to zero these
+		 * bytes - previous behaviour will be retained. However
+		 * this does invalidate the CRC and will force a config
+		 * download every time until the configuration is updated.
+		 */
+		if (cfg_data->size < object->size) {
+			dev_err(dev, "Warning: zeroing %d byte(s) in T%d\n",
+				 object->size - cfg_data->size, cfg_data->type);
+
+			for (i = cfg_data->size + 1; i < object->size; i++) {
+				ret = mxt_write_reg(data->client, reg + i, 0);
+				if (ret)
+					return ret;
+			}
+		}
+	}
+	dev_info(dev, "Updated configuration\n");
+
+	return ret;
+}
+
 static int mxt_check_reg_init(struct mxt_data *data)
 {
 	const struct mxt_platform_data *pdata = data->pdata;
@@ -664,7 +994,7 @@
 			continue;
 
 		for (j = 0;
-		     j < (object->size + 1) * (object->instances + 1);
+		     j < object->size * object->instances;
 		     j++) {
 			config_offset = index + j;
 			if (config_offset > pdata->config_length) {
@@ -674,7 +1004,7 @@
 			mxt_write_object(data, object->type, j,
 					 pdata->config[config_offset]);
 		}
-		index += (object->size + 1) * (object->instances + 1);
+		index += object->size * object->instances;
 	}
 
 	return 0;
@@ -684,7 +1014,7 @@
 {
 	struct device *dev = &data->client->dev;
 	struct mxt_message message;
-	int count = 10;
+	int count = data->max_reportid * 2;
 	int error;
 
 	/* Read dummy message to make high CHG pin */
@@ -718,12 +1048,14 @@
 			pdata->orient);
 
 	/* Set touchscreen burst length */
-	mxt_write_object(data, MXT_TOUCH_MULTI_T9,
-			MXT_TOUCH_BLEN, pdata->blen);
+	if (pdata->blen)
+		mxt_write_object(data, MXT_TOUCH_MULTI_T9,
+				MXT_TOUCH_BLEN, pdata->blen);
 
 	/* Set touchscreen threshold */
-	mxt_write_object(data, MXT_TOUCH_MULTI_T9,
-			MXT_TOUCH_TCHTHR, pdata->threshold);
+	if (pdata->threshold)
+		mxt_write_object(data, MXT_TOUCH_MULTI_T9,
+				MXT_TOUCH_TCHTHR, pdata->threshold);
 
 	/* Set touchscreen resolution */
 	mxt_write_object(data, MXT_TOUCH_MULTI_T9,
@@ -803,22 +1135,55 @@
 
 		object->type = buf[0];
 		object->start_address = (buf[2] << 8) | buf[1];
-		object->size = buf[3];
-		object->instances = buf[4];
+		/* the real size of object is buf[3]+1 */
+		object->size = buf[3] + 1;
+		/* the real instances of object is buf[4]+1 */
+		object->instances = buf[4] + 1;
 		object->num_report_ids = buf[5];
 
+		dev_dbg(&data->client->dev,
+			"obj %d addr 0x%x size %d insts %d num_reps %d\n",
+			object->type, object->start_address, object->size,
+			object->instances, object->num_report_ids);
+
 		if (object->num_report_ids) {
-			reportid += object->num_report_ids *
-					(object->instances + 1);
+			reportid += object->num_report_ids * object->instances;
 			object->max_reportid = reportid;
 		}
 	}
 
+	/* Store maximum reportid */
+	data->max_reportid = reportid;
 	return 0;
 }
 
-static int mxt_initialize(struct mxt_data *data)
+static void mxt_make_reportid_table(struct mxt_data *data)
 {
+	struct mxt_object *objects = data->object_table;
+	struct mxt_reportid *reportids = data->reportid_table;
+	struct device *dev = &data->client->dev;
+	int i, j;
+	int id = 0;
+
+	for (i = 0; i < data->info.object_num; i++) {
+		for (j = 0; j < objects[i].num_report_ids *
+				 objects[i].instances; j++) {
+			id++;
+			reportids[id].type = objects[i].type;
+			reportids[id].index = j;
+			dev_dbg(dev, "Report_id[%d]:\tT%d\tindex%d\n",
+				id, reportids[id].type,
+				reportids[id].index);
+		}
+	}
+
+	dev_dbg(&data->client->dev, "maXTouch: %d report ID\n",
+			data->max_reportid);
+}
+
+static int mxt_initialize(struct mxt_fw_info *fw_info)
+{
+	struct mxt_data *data = fw_info->data;
 	struct i2c_client *client = data->client;
 	struct mxt_info *info = &data->info;
 	int error;
@@ -841,11 +1206,33 @@
 	if (error)
 		return error;
 
-	/* Check register init values */
-	error = mxt_check_reg_init(data);
-	if (error)
-		return error;
+	data->reportid_table = kcalloc(data->max_reportid + 1,
+				     sizeof(struct mxt_reportid),
+				     GFP_KERNEL);
+	if (!data->reportid_table) {
+		dev_err(&client->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
 
+	/* Make the report id table infomation */
+	mxt_make_reportid_table(data);
+
+	/* Stop the event handler before backup memory */
+	mxt_write_object(data, MXT_GEN_COMMAND_T6,
+			MXT_COMMAND_BACKUPNV,
+			MXT_DISABLE_EVENT_VALUE);
+	msleep(MXT_BACKUP_TIME);
+
+	/* Check register init values */
+	if (fw_info->cfg_raw_data) {
+		error = mxt_download_config(fw_info);
+		if (error)
+			return error;
+	} else {
+		error = mxt_check_reg_init(data);
+		if (error)
+			return error;
+	}
 	mxt_handle_pdata(data);
 
 	/* Backup to memory */
@@ -924,7 +1311,7 @@
 			continue;
 		}
 
-		for (j = 0; j < object->size + 1; j++) {
+		for (j = 0; j < object->size; j++) {
 			error = mxt_read_object(data,
 						object->type, j, &val);
 			if (error)
@@ -943,47 +1330,103 @@
 
 	return count;
 }
-
-static int mxt_load_fw(struct device *dev, const char *fn)
+static int mxt_verify_fw(struct mxt_fw_info *fw_info, const struct firmware *fw)
 {
-	struct mxt_data *data = dev_get_drvdata(dev);
-	struct i2c_client *client = data->client;
-	const struct firmware *fw = NULL;
+	struct mxt_data *data = fw_info->data;
+	struct device *dev = &data->client->dev;
+	struct mxt_fw_image *fw_img;
+
+	if (!fw) {
+		dev_err(dev, "could not find firmware file\n");
+		return -ENOENT;
+	}
+
+	fw_img = (struct mxt_fw_image *)fw->data;
+
+	if (le32_to_cpu(fw_img->magic_code) != MXT_FW_MAGIC) {
+		/* In case, firmware file only consist of firmware */
+		dev_dbg(dev, "Firmware file only consist of raw firmware\n");
+		fw_info->fw_len = fw->size;
+		fw_info->fw_raw_data = fw->data;
+	} else {
+		/*
+		 * In case, firmware file consist of header,
+		 * configuration, firmware.
+		 */
+		dev_dbg(dev, "Firmware file consist of header, configuration, firmware\n");
+		fw_info->fw_ver = fw_img->fw_ver;
+		fw_info->build_ver = fw_img->build_ver;
+		fw_info->hdr_len = le32_to_cpu(fw_img->hdr_len);
+		fw_info->cfg_len = le32_to_cpu(fw_img->cfg_len);
+		fw_info->fw_len = le32_to_cpu(fw_img->fw_len);
+		fw_info->cfg_crc = le32_to_cpu(fw_img->cfg_crc);
+
+		/* Check the firmware file with header */
+		if (fw_info->hdr_len != sizeof(struct mxt_fw_image)
+			|| fw_info->hdr_len + fw_info->cfg_len
+				+ fw_info->fw_len != fw->size) {
+			dev_err(dev, "Firmware file is invaild !!hdr size[%d] cfg,fw size[%d,%d] filesize[%d]\n",
+				fw_info->hdr_len, fw_info->cfg_len,
+				fw_info->fw_len, fw->size);
+			return -EINVAL;
+		}
+
+		if (!fw_info->cfg_len) {
+			dev_err(dev, "Firmware file dose not include configuration data\n");
+			return -EINVAL;
+		}
+		if (!fw_info->fw_len) {
+			dev_err(dev, "Firmware file dose not include raw firmware data\n");
+			return -EINVAL;
+		}
+
+		/* Get the address of configuration data */
+		fw_info->cfg_raw_data = fw_img->data;
+
+		/* Get the address of firmware data */
+		fw_info->fw_raw_data = fw_img->data + fw_info->cfg_len;
+
+	}
+
+	return 0;
+}
+
+static int mxt_load_fw(struct mxt_fw_info *fw_info)
+{
+	struct mxt_data *data = fw_info->data;
+	struct i2c_client *client = data->client_boot;
+	struct device *dev = &data->client->dev;
+	const u8 *fw_data = fw_info->fw_raw_data;
+	size_t fw_size = fw_info->fw_len;
 	unsigned int frame_size;
 	unsigned int pos = 0;
 	int ret;
+	u8 val, retry_count = 5;
 
-	ret = request_firmware(&fw, fn, dev);
-	if (ret) {
-		dev_err(dev, "Unable to open firmware %s\n", fn);
-		return ret;
+	if (!fw_data) {
+		dev_err(dev, "firmware data is Null\n");
+		return -ENOMEM;
 	}
 
-	/* Change to the bootloader mode */
-	mxt_write_object(data, MXT_GEN_COMMAND_T6,
-			MXT_COMMAND_RESET, MXT_BOOT_VALUE);
-	msleep(MXT_RESET_TIME);
-
-	/* Change to slave address of bootloader */
-	if (client->addr == MXT_APP_LOW)
-		client->addr = MXT_BOOT_LOW;
-	else
-		client->addr = MXT_BOOT_HIGH;
-
 	ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD);
-	if (ret)
-		goto out;
+	if (ret) {
+		/*may still be unlocked from previous update attempt */
+		ret = mxt_check_bootloader(client, MXT_WAITING_FRAME_DATA);
+		if (ret)
+			goto out;
+	} else {
+		dev_info(dev, "Unlocking bootloader\n");
+		/* Unlock bootloader */
+		mxt_unlock_bootloader(client);
+	}
 
-	/* Unlock bootloader */
-	mxt_unlock_bootloader(client);
-
-	while (pos < fw->size) {
+	while (pos < fw_size) {
 		ret = mxt_check_bootloader(client,
 						MXT_WAITING_FRAME_DATA);
 		if (ret)
 			goto out;
 
-		frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1));
+		frame_size = ((*(fw_data + pos) << 8) | *(fw_data + pos + 1));
 
 		/* We should add 2 at frame size as the the firmware data is not
 		 * included the CRC bytes.
@@ -991,7 +1434,7 @@
 		frame_size += 2;
 
 		/* Write one frame to device */
-		mxt_fw_write(client, fw->data + pos, frame_size);
+		mxt_fw_write(client, fw_data + pos, frame_size);
 
 		ret = mxt_check_bootloader(client,
 						MXT_FRAME_CRC_PASS);
@@ -1000,18 +1443,95 @@
 
 		pos += frame_size;
 
-		dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size);
+		dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw_size);
+	}
+
+	/* Wait for IC enter to app mode */
+	do {
+		ret = mxt_read_reg(data->client, MXT_FAMILY_ID, &val);
+		if (!ret)
+			break;
+
+		dev_err(dev, "Waiting IC enter app mode\n");
+		msleep(MXT_FWRESET_TIME);
+
+	} while (--retry_count);
+
+	if (!retry_count) {
+		dev_err(dev, "No response after firmware update\n");
+		return -EIO;
 	}
 
 out:
-	release_firmware(fw);
+	return ret;
+}
 
-	/* Change to slave address of application */
-	if (client->addr == MXT_BOOT_LOW)
-		client->addr = MXT_APP_LOW;
-	else
-		client->addr = MXT_APP_HIGH;
+static int mxt_power_on(struct mxt_data *data)
+{
+	const struct mxt_platform_data *pdata = data->pdata;
+	struct device *dev = &data->client->dev;
+	int i, ret;
 
+	if (data->enabled)
+		return 0;
+
+	/* Enable regulators according to order */
+	for (i = 0; i < ARRAY_SIZE(data->supplies); i++) {
+		ret = regulator_enable(data->supplies[i].consumer);
+		if (ret) {
+			dev_err(dev, "Fail to enable regulator %s\n",
+				data->supplies[i].supply);
+			goto err;
+		}
+	}
+	/* Deassert reset pin */
+	if (pdata->gpio_reset) {
+		usleep_range(1000, 1500);
+		gpio_set_value(pdata->gpio_reset, 1);
+	}
+
+	if (pdata->reset_msec)
+		msleep(pdata->reset_msec);
+
+	data->enabled = true;
+
+	return 0;
+
+err:
+	while (--i >= 0)
+		regulator_disable(data->supplies[i].consumer);
+	return ret;
+}
+
+static int mxt_power_off(struct mxt_data *data)
+{
+	const struct mxt_platform_data *pdata = data->pdata;
+	int ret;
+
+	if (!data->enabled)
+		return 0;
+
+	/* Assert reset pin */
+	if (pdata->gpio_reset)
+		gpio_set_value(pdata->gpio_reset, 0);
+
+	/* Disable regulator */
+	ret = regulator_bulk_disable(ARRAY_SIZE(data->supplies),
+			 data->supplies);
+
+	if (ret)
+		goto err;
+
+	data->enabled = false;
+
+	return 0;
+
+err:
+	/* Deassert reset pin */
+	if (pdata->gpio_reset) {
+		usleep_range(1000, 1500);
+		gpio_set_value(pdata->gpio_reset, 1);
+	}
 	return ret;
 }
 
@@ -1020,29 +1540,85 @@
 					const char *buf, size_t count)
 {
 	struct mxt_data *data = dev_get_drvdata(dev);
+	struct mxt_fw_info fw_info;
+	const struct firmware *fw = NULL;
+	const char *firmware_name = data->pdata->firmware_name ?: MXT_FW_NAME;
 	int error;
+	struct input_dev *input_dev = data->input_dev;
+	bool enabled_status;
 
+	error = wait_for_completion_interruptible_timeout(&data->init_done,
+			msecs_to_jiffies(90 * MSEC_PER_SEC));
+
+	if (error <= 0) {
+		dev_err(dev, "error while waiting for device to init (%d)\n",
+			error);
+		return -EBUSY;
+	}
+	mutex_lock(&input_dev->mutex);
+
+	enabled_status = data->enabled;
+	if (!enabled_status) {
+		error = mxt_power_on(data);
+		if (error) {
+			dev_err(dev, "Failed to turn on touch\n");
+			goto err_power_on;
+		}
+	}
 	disable_irq(data->irq);
 
-	error = mxt_load_fw(dev, MXT_FW_NAME);
+	dev_info(dev, "Updating firmware from sysfs\n");
+
+	error = request_firmware(&fw, firmware_name, dev);
 	if (error) {
-		dev_err(dev, "The firmware update failed(%d)\n", error);
-		count = error;
-	} else {
-		dev_dbg(dev, "The firmware update succeeded\n");
-
-		/* Wait for reset */
-		msleep(MXT_FWRESET_TIME);
-
-		kfree(data->object_table);
-		data->object_table = NULL;
-
-		mxt_initialize(data);
+		dev_err(dev, "Unable to open firmware %s\n", firmware_name);
+		goto err_request_firmware;
 	}
 
-	enable_irq(data->irq);
+	memset(&fw_info, 0, sizeof(struct mxt_fw_info));
+	fw_info.data = data;
 
+	error = mxt_verify_fw(&fw_info, fw);
+	if (error)
+		goto err_verify_fw;
+
+	/* Change to the bootloader mode */
+	error = mxt_write_object(data, MXT_GEN_COMMAND_T6,
+			MXT_COMMAND_RESET, MXT_BOOT_VALUE);
+	if (error)
+		goto err_write_object;
+
+	msleep(MXT_RESET_TIME);
+
+	error = mxt_load_fw(&fw_info);
+	if (error) {
+		dev_err(dev, "The firmware update failed(%d)\n", error);
+	} else {
+		dev_info(dev, "The firmware update succeeded\n");
+		kfree(data->object_table);
+		data->object_table = NULL;
+		kfree(data->reportid_table);
+		data->reportid_table = NULL;
+
+		error = mxt_initialize(&fw_info);
+		if (error) {
+			dev_err(dev, "Failed to initialize\n");
+			goto err_initialize;
+		}
+	}
 	error = mxt_make_highchg(data);
+
+err_initialize:
+err_write_object:
+err_verify_fw:
+	release_firmware(fw);
+err_request_firmware:
+	enable_irq(data->irq);
+	if (!enabled_status)
+		mxt_power_off(data);
+err_power_on:
+	mutex_unlock(&input_dev->mutex);
+
 	if (error)
 		return error;
 
@@ -1062,27 +1638,76 @@
 	.attrs = mxt_attrs,
 };
 
-static void mxt_start(struct mxt_data *data)
+static int mxt_start(struct mxt_data *data)
 {
-	/* Touch enable */
-	mxt_write_object(data,
-			MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83);
+	int error = 0;
+
+	if (data->enabled) {
+		dev_err(&data->client->dev, "Touch is already started\n");
+		return error;
+	}
+
+	error = mxt_power_on(data);
+	if (error)
+		dev_err(&data->client->dev, "Fail to start touch\n");
+	else
+		enable_irq(data->irq);
+
+	return error;
 }
 
 static void mxt_stop(struct mxt_data *data)
 {
-	/* Touch disable */
-	mxt_write_object(data,
-			MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0);
+	int id, count = 0;
+
+	if (!data->enabled) {
+		dev_err(&data->client->dev, "Touch is already stopped\n");
+		return;
+	}
+	disable_irq(data->irq);
+	mxt_power_off(data);
+
+	/* release the finger which is remained */
+	for (id = 0; id < MXT_MAX_FINGER; id++) {
+		if (!data->finger[id].status)
+			continue;
+		data->finger[id].status = MXT_RELEASE;
+		count++;
+	}
+
+	if (count)
+		mxt_input_report(data);
 }
 
 static int mxt_input_open(struct input_dev *dev)
 {
 	struct mxt_data *data = input_get_drvdata(dev);
+	int ret;
 
-	mxt_start(data);
+	ret = wait_for_completion_interruptible_timeout(&data->init_done,
+			msecs_to_jiffies(90 * MSEC_PER_SEC));
+
+	if (ret < 0) {
+		dev_err(&dev->dev,
+			"error while waiting for device to init (%d)\n", ret);
+		ret = -ENXIO;
+		goto err_open;
+	}
+	if (ret == 0) {
+		dev_err(&dev->dev,
+			"timedout while waiting for device to init\n");
+		ret = -ENXIO;
+		goto err_open;
+	}
+
+	ret = mxt_start(data);
+	if (ret)
+		goto err_open;
 
 	return 0;
+
+err_open:
+	return ret;
 }
 
 static void mxt_input_close(struct input_dev *dev)
@@ -1092,23 +1717,233 @@
 	mxt_stop(data);
 }
 
+static int mxt_ts_finish_init(struct mxt_data *data)
+{
+	struct i2c_client *client = data->client;
+	int error;
+
+	error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
+			data->pdata->irqflags, client->dev.driver->name, data);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		goto err_req_irq;
+	}
+
+	error = mxt_make_highchg(data);
+	if (error) {
+		dev_err(&client->dev, "Failed to clear CHG pin\n");
+		goto err_req_irq;
+	}
+
+	dev_info(&client->dev,  "Mxt touch controller initialized\n");
+
+	/*
+	 * to prevent unnecessary report of touch event
+	 * it will be enabled in open function
+	 */
+	mxt_stop(data);
+
+	/* for blocking to be excuted open function untile finishing ts init */
+	complete_all(&data->init_done);
+	return 0;
+
+err_req_irq:
+	return error;
+}
+
+static int mxt_ts_rest_init(struct mxt_fw_info *fw_info)
+{
+	struct mxt_data *data = fw_info->data;
+	struct device *dev = &data->client->dev;
+	int error;
+
+	error = mxt_initialize(fw_info);
+	if (error) {
+		dev_err(dev, "Failed to initialize\n");
+		goto err_free_mem;
+	}
+
+	error = mxt_ts_finish_init(data);
+	if (error)
+		goto err_free_mem;
+
+	return 0;
+
+err_free_mem:
+	kfree(data->object_table);
+	data->object_table = NULL;
+	kfree(data->reportid_table);
+	data->reportid_table = NULL;
+	return error;
+}
+
+static int mxt_enter_bootloader(struct mxt_data *data)
+{
+	struct device *dev = &data->client->dev;
+	int error;
+
+	data->object_table = kcalloc(data->info.object_num,
+				     sizeof(struct mxt_object),
+				     GFP_KERNEL);
+	if (!data->object_table) {
+		dev_err(dev, "%s Failed to allocate memory\n",
+			__func__);
+		error = -ENOMEM;
+		goto out;
+	}
+
+	/* Get object table information*/
+	error = mxt_get_object_table(data);
+	if (error)
+		goto err_free_mem;
+
+	/* Change to the bootloader mode */
+	mxt_write_object(data, MXT_GEN_COMMAND_T6,
+			MXT_COMMAND_RESET, MXT_BOOT_VALUE);
+	msleep(MXT_RESET_TIME);
+
+err_free_mem:
+	kfree(data->object_table);
+	data->object_table = NULL;
+
+out:
+	return error;
+}
+
+static int mxt_update_fw_on_probe(struct mxt_fw_info *fw_info)
+{
+	struct mxt_data *data = fw_info->data;
+	struct device *dev = &data->client->dev;
+	int error;
+
+	error = mxt_get_info(data);
+	if (error) {
+		/* need to check IC is in boot mode */
+		error = mxt_probe_bootloader(data->client_boot);
+		if (error) {
+			dev_err(dev, "Failed to verify bootloader's status\n");
+			goto out;
+		}
+
+		dev_info(dev, "Updating firmware from boot-mode\n");
+		goto load_fw;
+	}
+
+	/* compare the version to verify necessity of firmware updating */
+	if (data->info.version == fw_info->fw_ver
+			&& data->info.build == fw_info->build_ver) {
+		dev_dbg(dev, "Firmware version is same with in IC\n");
+		goto out;
+	}
+
+	dev_info(dev, "Updating firmware from app-mode : IC:0x%x,0x%x =! FW:0x%x,0x%x\n",
+			data->info.version, data->info.build,
+			fw_info->fw_ver, fw_info->build_ver);
+
+	error = mxt_enter_bootloader(data);
+	if (error) {
+		dev_err(dev, "Failed updating firmware\n");
+		goto out;
+	}
+
+load_fw:
+	error = mxt_load_fw(fw_info);
+	if (error)
+		dev_err(dev, "Failed updating firmware\n");
+	else
+		dev_info(dev, "succeeded updating firmware\n");
+out:
+	return error;
+}
+
+static void mxt_request_firmware_work_func(const struct firmware *fw,
+		void *context)
+{
+	struct mxt_data *data = context;
+	struct mxt_fw_info fw_info;
+	int error;
+
+	memset(&fw_info, 0, sizeof(struct mxt_fw_info));
+	fw_info.data = data;
+
+	error = mxt_verify_fw(&fw_info, fw);
+	if (error)
+		goto ts_rest_init;
+
+	/* Skip update on boot up if firmware file does not have a header */
+	if (!fw_info.hdr_len)
+		goto ts_rest_init;
+
+	error = mxt_update_fw_on_probe(&fw_info);
+	if (error)
+		goto out;
+
+ts_rest_init:
+	error = mxt_ts_rest_init(&fw_info);
+out:
+	if (error)
+		/* complete anyway, so open() doesn't get blocked */
+		complete_all(&data->init_done);
+
+	release_firmware(fw);
+}
+
+static int __devinit mxt_ts_init(struct mxt_data *data)
+{
+	struct i2c_client *client = data->client;
+	const char *firmware_name = data->pdata->firmware_name ?: MXT_FW_NAME;
+	int ret = 0;
+
+	ret = request_firmware_nowait(THIS_MODULE, true, firmware_name,
+			      &client->dev, GFP_KERNEL,
+			      data, mxt_request_firmware_work_func);
+	if (ret)
+		dev_err(&client->dev,
+			"cannot schedule firmware update (%d)\n", ret);
+
+	return ret;
+}
+
 static int __devinit mxt_probe(struct i2c_client *client,
 		const struct i2c_device_id *id)
 {
 	const struct mxt_platform_data *pdata = client->dev.platform_data;
 	struct mxt_data *data;
 	struct input_dev *input_dev;
-	int error;
+	u16 boot_address;
+	int error, i;
 
 	if (!pdata)
 		return -EINVAL;
 
 	data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
-	input_dev = input_allocate_device();
-	if (!data || !input_dev) {
+	if (!data) {
 		dev_err(&client->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(mxt_supply_names); i++)
+		data->supplies[i].supply = mxt_supply_names[i];
+
+	error = regulator_bulk_get(&client->dev, ARRAY_SIZE(data->supplies),
+				 data->supplies);
+	if (error)
+		goto err_get_regulator;
+
+	if (pdata->gpio_reset) {
+		error = gpio_request_one(pdata->gpio_reset, GPIOF_OUT_INIT_LOW,
+					 "atmel_mxt_ts nRESET");
+		if (error) {
+			dev_err(&client->dev, "Unable to get the reset gpio.\n");
+			goto err_request_gpio;
+		}
+	}
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
 		error = -ENOMEM;
-		goto err_free_mem;
+		goto err_allocate_input_device;
 	}
 
 	input_dev->name = "Atmel maXTouch Touchscreen";
@@ -1121,22 +1956,13 @@
 	data->input_dev = input_dev;
 	data->pdata = pdata;
 	data->irq = client->irq;
+	init_completion(&data->init_done);
 
 	mxt_calc_resolution(data);
 
 	__set_bit(EV_ABS, input_dev->evbit);
-	__set_bit(EV_KEY, input_dev->evbit);
-	__set_bit(BTN_TOUCH, input_dev->keybit);
+	__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
 
-	/* For single touch */
-	input_set_abs_params(input_dev, ABS_X,
-			     0, data->max_x, 0, 0);
-	input_set_abs_params(input_dev, ABS_Y,
-			     0, data->max_y, 0, 0);
-	input_set_abs_params(input_dev, ABS_PRESSURE,
-			     0, 255, 0, 0);
-
-	/* For multi touch */
 	input_mt_init_slots(input_dev, MXT_MAX_FINGER);
 	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
 			     0, MXT_MAX_AREA, 0, 0);
@@ -1146,45 +1972,67 @@
 			     0, data->max_y, 0, 0);
 	input_set_abs_params(input_dev, ABS_MT_PRESSURE,
 			     0, 255, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+			     0, 255, 0, 0);
 
 	input_set_drvdata(input_dev, data);
 	i2c_set_clientdata(client, data);
 
-	error = mxt_initialize(data);
-	if (error)
-		goto err_free_object;
-
-	error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
-			pdata->irqflags, client->dev.driver->name, data);
-	if (error) {
-		dev_err(&client->dev, "Failed to register interrupt\n");
-		goto err_free_object;
+	if (data->pdata->boot_address) {
+		boot_address = data->pdata->boot_address;
+	} else {
+		if (client->addr == MXT_APP_LOW)
+			boot_address = MXT_BOOT_LOW;
+		else
+			boot_address = MXT_BOOT_HIGH;
 	}
 
-	error = mxt_make_highchg(data);
-	if (error)
-		goto err_free_irq;
+	data->client_boot = i2c_new_dummy(client->adapter, boot_address);
+	if (!data->client_boot) {
+		dev_err(&client->dev, "Fail to register sub client[0x%x]\n",
+			 boot_address);
+		error = -ENODEV;
+		goto err_create_sub_client;
+	}
 
 	error = input_register_device(input_dev);
 	if (error)
-		goto err_free_irq;
+		goto err_register_input_device;
 
 	error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);
 	if (error)
-		goto err_unregister_device;
+		goto err_create_attr_group;
+
+	error = mxt_power_on(data);
+	if (error)
+		goto err_power_on;
+
+	error = mxt_ts_init(data);
+	if (error)
+		goto err_init;
 
 	return 0;
 
-err_unregister_device:
+err_init:
+	mxt_power_off(data);
+err_power_on:
+	sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
+err_create_attr_group:
 	input_unregister_device(input_dev);
 	input_dev = NULL;
-err_free_irq:
-	free_irq(client->irq, data);
-err_free_object:
-	kfree(data->object_table);
-err_free_mem:
+
+err_register_input_device:
+	i2c_unregister_device(data->client_boot);
+err_create_sub_client:
 	input_free_device(input_dev);
+err_allocate_input_device:
+	if (pdata->gpio_reset)
+		gpio_free(pdata->gpio_reset);
+err_request_gpio:
+	regulator_bulk_free(ARRAY_SIZE(data->supplies), data->supplies);
+err_get_regulator:
 	kfree(data);
+
 	return error;
 }
 
@@ -1193,9 +2041,16 @@
 	struct mxt_data *data = i2c_get_clientdata(client);
 
 	sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
+	enable_irq(data->irq);
 	free_irq(data->irq, data);
 	input_unregister_device(data->input_dev);
+	i2c_unregister_device(data->client_boot);
 	kfree(data->object_table);
+	kfree(data->reportid_table);
+	mxt_power_off(data);
+	regulator_bulk_free(ARRAY_SIZE(data->supplies), data->supplies);
+	if (data->pdata->gpio_reset)
+		gpio_free(data->pdata->gpio_reset);
 	kfree(data);
 
 	return 0;
@@ -1224,12 +2079,6 @@
 	struct mxt_data *data = i2c_get_clientdata(client);
 	struct input_dev *input_dev = data->input_dev;
 
-	/* Soft reset */
-	mxt_write_object(data, MXT_GEN_COMMAND_T6,
-			MXT_COMMAND_RESET, 1);
-
-	msleep(MXT_RESET_TIME);
-
 	mutex_lock(&input_dev->mutex);
 
 	if (input_dev->users)
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index ef2ddd4..16a91a8 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -394,6 +394,14 @@
 	  This option enables support for on-chip LED drivers on
 	  MAXIM MAX8997 PMIC.
 
+config LEDS_AS3668
+	tristate "LED Support for Austriamicrosystems AS3668"
+	depends on LEDS_CLASS && LEDS_TRIGGERS && I2C
+	help
+	  This option enables support for LEDs connected to Austriamicrosystems
+	  AS3668 led controller accessed via the I2C bus.
+	  Brightness control and hardware-assisted blinking are supported.
+
 config LEDS_OT200
 	tristate "LED support for the Bachmann OT200"
 	depends on LEDS_CLASS && HAS_IOMEM
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 9d3b1094..e22d47f 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -45,6 +45,7 @@
 obj-$(CONFIG_LEDS_ASIC3)		+= leds-asic3.o
 obj-$(CONFIG_LEDS_RENESAS_TPU)		+= leds-renesas-tpu.o
 obj-$(CONFIG_LEDS_MAX8997)		+= leds-max8997.o
+obj-$(CONFIG_LEDS_AS3668)		+= leds-as3668.o
 
 # LED SPI Drivers
 obj-$(CONFIG_LEDS_DAC124S085)		+= leds-dac124s085.o
diff --git a/drivers/leds/leds-as3668.c b/drivers/leds/leds-as3668.c
new file mode 100644
index 0000000..b42e9c3
--- /dev/null
+++ b/drivers/leds/leds-as3668.c
@@ -0,0 +1,640 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics. All rights reserved.
+ *
+ * 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
+ */
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/leds-as3668.h>
+
+#define AS3668_DRV_NAME				"as3668"
+#define AS3668_CHIPID1				0xA5
+
+/* AS3668 registers */
+#define AS3668_REG_CURRX_CTRL			0x01
+#define AS3668_REG_CURR1_CURRENT		0x02
+#define AS3668_REG_CURR2_CURRENT		0x03
+#define AS3668_REG_CURR3_CURRENT		0x04
+#define AS3668_REG_CURR4_CURRENT		0x05
+#define AS3668_REG_GPIO_CTRL			0x06
+#define AS3668_REG_PWM_CTRL			0x15
+#define AS3668_REG_PWM_TIMING			0x16
+#define AS3668_REG_PATTERN_TIMING		0x18
+#define AS3668_REG_FRAME_MASK			0x1A
+#define AS3668_REG_PATTERN_START_CTRL		0x1B
+#define AS3668_REG_FRAME_START_DELAY		0x1C
+#define AS3668_REG_OVERTEMP_CTRL		0x29
+#define AS3668_REG_CHIPID1			0x3E
+#define AS3668_REG_CHIPID2			0x3F
+#define AS3668_REG_AUDIO_INPUT_BUFFER		0x41
+#define AS3668_REG_AUDIO_CTRL			0x42
+#define AS3668_REG_AUDIO_OUTPUT			0x43
+
+#define AS3668_PATTERN_START_ON			0x02
+
+struct as3668_led {
+	struct i2c_client *client;
+	struct mutex lock;
+	struct led_classdev ldev;
+	u32 color;		/* 0xRRGGBBWW */
+	u8 led_array[AS3668_LED_NUM];	/* C1 C2 C3 C4 */
+	u8 pwm_dim_speed;
+	u8 pattern_frame_mask;	/* 0b F4 F4 F3 F3 F2 F2 F1 F1 */
+	u8 pattern_frame_delay;	/* 0b D4 D4 D3 D3 D2 D2 D1 D0 */
+	bool blinking;
+	u32 pattern;
+};
+
+enum AS3668_MODE {
+	MODE_OFF		= 0x00,
+	MODE_ON			= 0x55,
+	MODE_PWM		= 0xAA,
+	MODE_PATTERN		= 0xFF,
+};
+
+/* voodoo from AS3668 data sheet Revision 1.11, page 41 */
+static const u16 pattern_toff_time[8] = {80, 150, 280, 540,
+			1100, 2100, 4200, 8400};
+static const u16 pattern_ton_time[8] = {40, 70, 140, 270,
+			530, 1100, 2100, 4200};
+/* voodoo from AS3668 data sheet Revision 1.11, page 39 */
+static const u16 pattern_dim_speed_time[16] = {0, 120, 250,
+			380, 510, 770, 1000, 1600, 2100, 2600,
+			3100, 4200, 5200, 6200, 7300, 8300};
+
+static s32 as3668_i2c_update_bits(struct i2c_client *client,
+		u8 addr, u8 mask, u8 val)
+{
+	s32 ret;
+	u8 value;
+	ret = i2c_smbus_read_byte_data(client, addr);
+	if (ret < 0) {
+		pr_err("Can't read 0x%02x via i2c\n", addr);
+		return ret;
+	}
+
+	value = ret & ~mask;
+	value |= (val & mask);
+
+	ret = i2c_smbus_write_byte_data(client, addr, value);
+	if (ret) {
+		pr_err("Can't write to 0x%02x via i2c\n", addr);
+		return ret;
+	}
+	return 0;
+}
+
+static int as3668_binary_search(u16 value, const u16 array[], int low, int high)
+{
+	int mid;
+	if (value >= array[high])
+		return high;
+	else if (value <= array[low])
+		return low;
+
+	while (high > low + 1) {
+		mid = (high + low) / 2;
+		if (value < array[mid])
+			high = mid;
+		else
+			low = mid;
+	}
+	if ((array[high] - value) > (value - array[low]))
+		mid = low;
+	else
+		mid = high;
+
+	return mid;
+}
+
+static s32 as3668_set_color(const struct as3668_led *led, u8 weight,
+		u32 color)
+{
+	int ret;
+	int i;
+	for (i = AS3668_REG_CURR1_CURRENT; i <= AS3668_REG_CURR4_CURRENT; i++) {
+		ret = i2c_smbus_write_byte_data(led->client, i,
+			((weight + 1) * (u8)(color >>
+			led->led_array[i - AS3668_REG_CURR1_CURRENT]))
+			>> 8);
+		if (ret) {
+			pr_err("Fail to set color\n");
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static int as3668_update(struct as3668_led *led)
+{
+	int ret;
+	enum AS3668_MODE mode;
+
+	if (led->ldev.brightness == 0)
+		mode = MODE_OFF;
+	else if (led->blinking)
+		mode = MODE_PATTERN;
+	else
+		mode = MODE_ON;
+
+	if (led->blinking) {
+		ret = i2c_smbus_write_byte_data(led->client,
+			AS3668_REG_PATTERN_TIMING, led->pattern);
+		if (ret) {
+			pr_err("Can't update AS3668_REG_CURRX_CTRL\n");
+			return ret;
+		}
+	}
+
+	ret = i2c_smbus_write_byte_data(led->client, AS3668_REG_CURRX_CTRL,
+		mode);
+	if (ret) {
+		pr_err("Can't write AS3668_REG_CURRX_CTRL\n");
+		return ret;
+	}
+
+	ret = as3668_set_color(led, led->ldev.brightness, led->color);
+	if (ret) {
+		pr_err("Can't set color\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void as3668_set_led_brightness(struct led_classdev *led_cdev,
+		enum led_brightness value)
+{
+	struct as3668_led *led =
+	    container_of(led_cdev, struct as3668_led, ldev);
+
+	mutex_lock(&led->lock);
+	if (value == 0)
+		led->blinking = false;
+	led->ldev.brightness = (u8)value;
+	as3668_update(led);
+	mutex_unlock(&led->lock);
+}
+
+static int as3668_set_led_blink(struct led_classdev *led_cdev,
+		unsigned long *delay_on,
+		unsigned long *delay_off)
+{
+	struct as3668_led *led =
+	    container_of(led_cdev, struct as3668_led, ldev);
+	int ret;
+	int pattern_on;
+	int pattern_off;
+
+	mutex_lock(&led->lock);
+	led->blinking = true;
+
+	pattern_on = as3668_binary_search(*delay_on, pattern_ton_time,
+				0, ARRAY_SIZE(pattern_ton_time) - 1);
+	*delay_on = pattern_ton_time[pattern_on];
+
+	pattern_off = as3668_binary_search(*delay_off,
+			pattern_toff_time, 0,
+			ARRAY_SIZE(pattern_toff_time) - 1);
+	*delay_off = pattern_toff_time[pattern_off];
+
+	led->pattern = (0x07 & pattern_on) | (0x38 & (pattern_off << 3));
+	ret = as3668_update(led);
+	mutex_unlock(&led->lock);
+
+	return ret;
+}
+
+static ssize_t as3668_color_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct as3668_led *led =
+	    container_of(led_cdev, struct as3668_led, ldev);
+
+	return sprintf(buf, "0x%08x\n", led->color);
+}
+
+static ssize_t as3668_color_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct as3668_led *led =
+	    container_of(led_cdev, struct as3668_led, ldev);
+	unsigned int new_value;
+	int err;
+
+	err = kstrtouint(buf, 16, &new_value);
+	if (err)
+		return err;
+
+	mutex_lock(&led->lock);
+	led->color = new_value;
+	as3668_update(led);
+	mutex_unlock(&led->lock);
+	return size;
+}
+
+static ssize_t as3668_slope_up_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct as3668_led *led =
+	    container_of(led_cdev, struct as3668_led, ldev);
+	return sprintf(buf, "%d\n",
+		pattern_dim_speed_time[(led->pwm_dim_speed & 0xF0) >> 4]);
+}
+
+static ssize_t as3668_slope_up_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct as3668_led *led =
+	    container_of(led_cdev, struct as3668_led, ldev);
+	u16 new_value;
+	int err;
+	int index;
+
+	err = kstrtou16(buf, 10, &new_value);
+	if (err)
+		return err;
+
+	index = as3668_binary_search(new_value, pattern_dim_speed_time,
+			0, ARRAY_SIZE(pattern_dim_speed_time) - 1);
+
+	mutex_lock(&led->lock);
+	led->pwm_dim_speed &= 0x0F;
+	led->pwm_dim_speed |= (0xF0 & index << 4);
+	i2c_smbus_write_byte_data(led->client, AS3668_REG_PWM_TIMING,
+				led->pwm_dim_speed);
+	mutex_unlock(&led->lock);
+
+	return size;
+}
+
+static ssize_t as3668_slope_down_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct as3668_led *led =
+		container_of(led_cdev, struct as3668_led, ldev);
+	return sprintf(buf, "%d\n",
+		pattern_dim_speed_time[led->pwm_dim_speed & 0x0F]);
+}
+
+static ssize_t as3668_slope_down_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct as3668_led *led =
+		container_of(led_cdev, struct as3668_led, ldev);
+	u16 new_value;
+	int err;
+	int index;
+
+	err = kstrtou16(buf, 10, &new_value);
+	if (err)
+		return err;
+
+	index = as3668_binary_search(new_value, pattern_dim_speed_time,
+			0, ARRAY_SIZE(pattern_dim_speed_time) - 1);
+
+	mutex_lock(&led->lock);
+	led->pwm_dim_speed &= 0xF0;
+	led->pwm_dim_speed |= 0x0F & index;
+	i2c_smbus_write_byte_data(led->client, AS3668_REG_PWM_TIMING,
+				led->pwm_dim_speed);
+	mutex_unlock(&led->lock);
+
+	return size;
+}
+
+static ssize_t as3668_frame_mask_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct as3668_led *led =
+	    container_of(led_cdev, struct as3668_led, ldev);
+	return sprintf(buf, "0x%08x\n", led->pattern_frame_mask);
+}
+
+static ssize_t as3668_frame_mask_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct as3668_led *led =
+	    container_of(led_cdev, struct as3668_led, ldev);
+	u8 new_value;
+	int err;
+
+	err = kstrtou8(buf, 16, &new_value);
+	if (err)
+		return err;
+
+	mutex_lock(&led->lock);
+	led->pattern_frame_mask = new_value;
+	i2c_smbus_write_byte_data(led->client, AS3668_REG_FRAME_MASK,
+					new_value);
+	mutex_unlock(&led->lock);
+
+	return size;
+}
+
+static ssize_t as3668_frame_delay_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct as3668_led *led =
+	    container_of(led_cdev, struct as3668_led, ldev);
+	return sprintf(buf, "0x%08x\n", led->pattern_frame_delay);
+}
+
+static ssize_t as3668_frame_delay_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct as3668_led *led =
+	    container_of(led_cdev, struct as3668_led, ldev);
+	u8 new_value;
+	int err;
+
+	err = kstrtou8(buf, 16, &new_value);
+	if (err)
+		return err;
+
+	mutex_lock(&led->lock);
+	led->pattern_frame_delay = new_value;
+	i2c_smbus_write_byte_data(led->client, AS3668_REG_FRAME_START_DELAY,
+					new_value);
+	mutex_unlock(&led->lock);
+
+	return size;
+}
+
+static int __devinit as3668_initialize(struct i2c_client *client,
+		struct as3668_platform_data *pdata)
+{
+	s32 ret;
+
+	ret = i2c_smbus_read_byte_data(client, AS3668_REG_CHIPID1);
+	if (ret < 0) {
+		pr_err("Fail to read chip ID1\n");
+		goto err_init;
+	}
+	if (ret != AS3668_CHIPID1) {
+		pr_err("Unsupported chip_id=0x%02x).\n", ret);
+		ret = -ENODEV;
+		goto err_init;
+	}
+	ret = i2c_smbus_read_byte_data(client, AS3668_REG_CHIPID2);
+	if (ret < 0) {
+		pr_err("Fail to read chip ID2\n");
+		goto err_init;
+	}
+	pr_debug("AS3668 chip id2:0x%02x rev:%d\n", ret & 0xF0, ret & 0x0F);
+
+	ret = i2c_smbus_write_byte_data(client, AS3668_REG_CURRX_CTRL,
+			 MODE_OFF);
+	if (ret) {
+		pr_err("Can't write AS3668_REG_CURRX_CTRL\n");
+		goto err_init;
+	}
+	ret = as3668_i2c_update_bits(client, AS3668_REG_PATTERN_START_CTRL,
+			0x03, pdata->pattern_start_source |
+			AS3668_PATTERN_START_ON);
+	if (ret) {
+		pr_err("Can't update AS3668_REG_PATTERN_START_CTRL\n");
+		goto err_init;
+	}
+	ret = as3668_i2c_update_bits(client, AS3668_REG_PWM_CTRL, 0x01,
+			pdata->pwm_source);
+	if (ret) {
+		pr_err("Can't update AS3668_REG_PWM_CTRL\n");
+		goto err_init;
+	}
+	ret = as3668_i2c_update_bits(client, AS3668_REG_GPIO_CTRL, 0x07,
+			pdata->gpio_input_invert << 2 |
+			pdata->gpio_input_mode << 1 |
+			pdata->gpio_mode);
+	if (ret) {
+		pr_err("Can't update AS3668_REG_GPIO_CTRL\n");
+		goto err_init;
+	}
+	ret = as3668_i2c_update_bits(client, AS3668_REG_AUDIO_CTRL, 0xE0,
+			pdata->audio_input_pin << 7 |
+			pdata->audio_pulldown_off << 6 |
+			pdata->audio_adc_characteristic << 5);
+	if (ret) {
+		pr_err("Can't update AS3668_REG_AUDIO_CTRL\n");
+		goto err_init;
+	}
+	ret = as3668_i2c_update_bits(client, AS3668_REG_AUDIO_INPUT_BUFFER,
+			0xC0,
+			pdata->audio_dis_start << 7 |
+			pdata->audio_man_start << 6);
+	if (ret) {
+		pr_err("Can't update AS3668_REG_AUDIO_INPUT_BUFFER\n");
+		goto err_init;
+	}
+
+	ret = as3668_i2c_update_bits(client, AS3668_REG_OVERTEMP_CTRL, 0x70,
+			pdata->vbat_monitor_voltage_index << 5 |
+			pdata->shutdown_enable << 4);
+	if (ret) {
+		pr_err("Can't update AS3668_REG_OVERTEMP_CTRL\n");
+		goto err_init;
+	}
+
+	return 0;
+
+err_init:
+	return ret;
+}
+
+static int __devinit as3668_led_initialize(struct i2c_client *client,
+		struct as3668_led *led, struct as3668_platform_data *pdata)
+{
+	int ret;
+
+	led->client = client;
+	led->color = 0xFFFFFFFF;
+	led->ldev.name = AS3668_DRV_NAME;
+	led->ldev.max_brightness = LED_FULL;
+	led->ldev.brightness = LED_OFF;
+	led->ldev.brightness_set = as3668_set_led_brightness;
+	led->ldev.blink_brightness = LED_FULL;
+	led->ldev.blink_set = as3668_set_led_blink;
+	memcpy(led->led_array, pdata->led_array, AS3668_LED_NUM);
+
+	ret = i2c_smbus_read_byte_data(client, AS3668_REG_PWM_TIMING);
+	if (ret < 0) {
+		pr_err("Fail to read AS3668_REG_PWM_TIMING\n");
+		return ret;
+	}
+	led->pwm_dim_speed = ret;
+	led->ldev.default_trigger = "none";
+	ret = i2c_smbus_read_byte_data(client, AS3668_REG_FRAME_MASK);
+	if (ret < 0) {
+		pr_err("Fail to read AS3668_REG_FRAME_MASK\n");
+		return ret;
+	}
+	led->pattern_frame_mask = ret;
+	ret = i2c_smbus_read_byte_data(client, AS3668_REG_FRAME_START_DELAY);
+	if (ret < 0) {
+		pr_err("Fail to read AS3668_REG_FRAME_START_DELAY\n");
+		return ret;
+	}
+	led->pattern_frame_delay = ret;
+
+	ret = led_classdev_register(&client->dev, &led->ldev);
+	if (ret) {
+		pr_err("Couldn't register LED %s\n", led->ldev.name);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void as3668_led_cleanup(struct as3668_led *led)
+{
+	as3668_set_led_brightness(&led->ldev, LED_OFF);
+	led_classdev_unregister(&led->ldev);
+}
+
+static DEVICE_ATTR(color, S_IRUGO | S_IWUSR | S_IWGRP,
+		as3668_color_show, as3668_color_store);
+static DEVICE_ATTR(slope_up, S_IRUGO | S_IWUSR | S_IWGRP,
+		as3668_slope_up_show, as3668_slope_up_store);
+static DEVICE_ATTR(slope_down, S_IRUGO | S_IWUSR | S_IWGRP,
+		as3668_slope_down_show, as3668_slope_down_store);
+static DEVICE_ATTR(frame_mask, S_IRUGO | S_IWUSR | S_IWGRP,
+		as3668_frame_mask_show, as3668_frame_mask_store);
+static DEVICE_ATTR(frame_delay, S_IRUGO | S_IWUSR | S_IWGRP,
+		as3668_frame_delay_show, as3668_frame_delay_store);
+
+static struct attribute *as3668_sysfs_attrs[] = {
+	&dev_attr_color.attr,
+	&dev_attr_slope_up.attr,
+	&dev_attr_slope_down.attr,
+	&dev_attr_frame_mask.attr,
+	&dev_attr_frame_delay.attr,
+	NULL
+};
+
+static struct attribute_group as3668_attribute_group = {
+	.attrs = as3668_sysfs_attrs,
+};
+
+static int __devinit as3668_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct as3668_led *led;
+	struct as3668_platform_data *pdata = client->dev.platform_data;
+	int ret;
+
+	if (pdata == NULL) {
+		pr_err("No platform data\n");
+		return -EINVAL;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+			I2C_FUNC_SMBUS_BYTE_DATA)) {
+		pr_err("Client not i2c capable\n");
+		return -ENOSYS;
+	}
+
+	led = kzalloc(sizeof(*led), GFP_KERNEL);
+	if (!led) {
+		pr_err("Failed to allocate memory for module\n");
+		return -ENOMEM;
+	}
+
+	ret = as3668_initialize(client, pdata);
+	if (ret) {
+		pr_err("Fail to initialize as3668\n");
+		return ret;
+	}
+
+	i2c_set_clientdata(client, led);
+	mutex_init(&led->lock);
+
+	ret = as3668_led_initialize(client, led, pdata);
+	if (ret) {
+		pr_err("Fail to initialize led device\n");
+		goto err_led_init;
+	}
+
+	ret = sysfs_create_group(&led->ldev.dev->kobj,
+			&as3668_attribute_group);
+	if (ret) {
+		pr_err("could not create sysfs group\n");
+		goto err_sysfs_create_group;
+	}
+	return 0;
+
+err_sysfs_create_group:
+	as3668_led_cleanup(led);
+err_led_init:
+	mutex_destroy(&led->lock);
+	kfree(led);
+	return ret;
+}
+
+static int __devexit as3668_remove(struct i2c_client *client)
+{
+	struct as3668_led *led = i2c_get_clientdata(client);
+
+	sysfs_remove_group(&led->ldev.dev->kobj,
+			&as3668_attribute_group);
+	as3668_led_cleanup(led);
+	mutex_destroy(&led->lock);
+	kfree(led);
+	return 0;
+}
+
+static void as3668_shutdown(struct i2c_client *client)
+{
+	struct as3668_led *led = i2c_get_clientdata(client);
+
+	as3668_set_led_brightness(&led->ldev, LED_OFF);
+}
+
+static const struct i2c_device_id as3668_id[] = {
+	{"as3668", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, as3668_id);
+static struct i2c_driver as3668_driver = {
+	.driver = {
+		.name = AS3668_DRV_NAME,
+		.owner  = THIS_MODULE,
+	},
+	.probe = as3668_probe,
+	.remove = __devexit_p(as3668_remove),
+	.shutdown = as3668_shutdown,
+	.id_table = as3668_id,
+};
+
+module_i2c_driver(as3668_driver);
+
+MODULE_AUTHOR("Hyoung Wook Ham <hwham@sta.samsung.com>");
+MODULE_DESCRIPTION("AS3668 LED driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/exynos/tv/Kconfig b/drivers/media/video/exynos/tv/Kconfig
index aed965b..079bfd4 100644
--- a/drivers/media/video/exynos/tv/Kconfig
+++ b/drivers/media/video/exynos/tv/Kconfig
@@ -28,6 +28,7 @@
 	depends on VIDEO_EXYNOS_TV
 	depends on SWITCH
 	select VIDEO_EXYNOS_HDMIPHY
+	select FB_MODE_HELPERS
 	help
 	  Say Y here if you want support for the HDMI output
 	  interface in S5P Samsung SoC. The driver can be compiled
@@ -38,7 +39,7 @@
 config VIDEO_EXYNOS_HDMI_AUDIO_I2S
 	bool "Enable HDMI audio using I2S path"
 	depends on VIDEO_EXYNOS_HDMI
-	depends on SND_SOC_SAMSUNG_SMDK_WM8994
+	depends on SND_SOC_SAMSUNG_SMDK_WM8994 || SND_SOC_SAMSUNG_MANTA_WM1811
 	default y
 	help
 	  Enables HDMI audio through I2S path.
diff --git a/drivers/media/video/exynos/tv/Makefile b/drivers/media/video/exynos/tv/Makefile
index 29cef3f..0fb127a 100644
--- a/drivers/media/video/exynos/tv/Makefile
+++ b/drivers/media/video/exynos/tv/Makefile
@@ -8,7 +8,7 @@
 obj-$(CONFIG_VIDEO_EXYNOS_HDMIPHY) += s5p-hdmiphy.o
 s5p-hdmiphy-y += hdmiphy_drv.o
 obj-$(CONFIG_VIDEO_EXYNOS_HDMI) += s5p-hdmi.o
-s5p-hdmi-y += hdcp_drv.o hdmi_drv.o
+s5p-hdmi-y += hdcp_drv.o hdmi_drv.o hdmi_edid.o
 obj-$(CONFIG_VIDEO_EXYNOS_SDO) += s5p-sdo.o
 s5p-sdo-y += sdo_drv.o
 obj-$(CONFIG_VIDEO_EXYNOS_MIXER) += s5p-mixer.o
diff --git a/drivers/media/video/exynos/tv/hdcp_drv.c b/drivers/media/video/exynos/tv/hdcp_drv.c
index 00cd209..d0d3190 100644
--- a/drivers/media/video/exynos/tv/hdcp_drv.c
+++ b/drivers/media/video/exynos/tv/hdcp_drv.c
@@ -416,12 +416,13 @@
 {
 	struct device *dev = hdev->dev;
 	u8 val;
-	unsigned long spin_flags;
 
-	if (!is_hdmi_streaming(hdev))
+	mutex_lock(&hdev->mutex);
+
+	if (!is_hdmi_streaming(hdev)) {
+		mutex_unlock(&hdev->mutex);
 		return -ENODEV;
-
-	spin_lock_irqsave(&hdev->hdcp_info.reset_lock, spin_flags);
+	}
 
 	hdev->hdcp_info.event		= HDCP_EVENT_STOP;
 	hdev->hdcp_info.auth_status	= NOT_AUTHENTICATED;
@@ -441,7 +442,7 @@
 	hdmi_writeb(hdev, HDMI_HDCP_CHECK_RESULT, HDMI_HDCP_CLR_ALL_RESULTS);
 
 	/* need some delay (at least 1 frame) */
-	mdelay(16);
+	msleep(16);
 
 	hdcp_sw_reset(hdev);
 
@@ -449,7 +450,8 @@
 		HDMI_WATCHDOG_INT_EN | HDMI_WTFORACTIVERX_INT_EN;
 	hdmi_write_mask(hdev, HDMI_STATUS_EN, ~0, val);
 	hdmi_write_mask(hdev, HDMI_HDCP_CTRL1, ~0, HDMI_HDCP_CP_DESIRED_EN);
-	spin_unlock_irqrestore(&hdev->hdcp_info.reset_lock, spin_flags);
+
+	mutex_unlock(&hdev->mutex);
 
 	return 0;
 }
@@ -916,14 +918,12 @@
 
 int hdcp_prepare(struct hdmi_device *hdev)
 {
-	hdev->hdcp_wq = create_workqueue("khdcpd");
+	hdev->hdcp_wq = create_singlethread_workqueue("khdcpd");
 	if (hdev->hdcp_wq == NULL)
 		return -ENOMEM;
 
 	INIT_WORK(&hdev->work, hdcp_work);
 
-	spin_lock_init(&hdev->hdcp_info.reset_lock);
-
 #if defined(CONFIG_VIDEO_EXYNOS_HDCP)
 	hdev->hdcp_info.hdcp_enable = 1;
 #else
diff --git a/drivers/media/video/exynos/tv/hdmi.h b/drivers/media/video/exynos/tv/hdmi.h
index 1858ecc..f388a0b 100644
--- a/drivers/media/video/exynos/tv/hdmi.h
+++ b/drivers/media/video/exynos/tv/hdmi.h
@@ -21,6 +21,7 @@
 #include <linux/io.h>
 #include <linux/clk.h>
 #include <linux/interrupt.h>
+#include <linux/mutex.h>
 #include <linux/regulator/consumer.h>
 #include <linux/switch.h>
 
@@ -29,6 +30,9 @@
 
 #define INFOFRAME_CNT          2
 
+/* default preset configured on probe */
+#define HDMI_DEFAULT_PRESET	V4L2_DV_720P60
+
 #define HDMI_VSI_VERSION	0x01
 #define HDMI_AVI_VERSION	0x02
 #define HDMI_AUI_VERSION	0x01
@@ -36,10 +40,14 @@
 #define HDMI_AVI_LENGTH		0x0d
 #define HDMI_AUI_LENGTH		0x0a
 
+#define AVI_UNDERSCAN			(2 << 0)
 #define AVI_ACTIVE_FORMAT_VALID		(1 << 4)
 #define AVI_PIC_ASPECT_RATIO_4_3	(1 << 4)
 #define AVI_PIC_ASPECT_RATIO_16_9	(2 << 4)
 #define AVI_SAME_AS_PIC_ASPECT_RATIO	8
+#define AVI_LIMITED_RANGE		(1 << 2)
+#define AVI_FULL_RANGE			(2 << 2)
+#define AVI_ITU709			(2 << 6)
 
 /* HDMI audio configuration value */
 #define DEFAULT_SAMPLE_RATE	44100
@@ -258,7 +266,6 @@
 	u8 is_repeater;
 	u32 hdcp_start;
 	int hdcp_enable;
-	spinlock_t reset_lock;
 
 	enum HDCP_EVENT	event;
 	enum HDCP_STATE	auth_status;
@@ -294,6 +301,8 @@
 	struct hdmi_infoframe infoframe[INFOFRAME_CNT];
 	/** audio on/off control flag */
 	int audio_enable;
+	/** number of audio channels */
+	int audio_channel_count;
 	/** audio sample rate */
 	int sample_rate;
 	/** audio bits per sample */
@@ -312,11 +321,15 @@
 
 	/* HPD releated */
 	struct work_struct hpd_work;
-	struct work_struct hpd_work_ext;
+	struct delayed_work hpd_work_ext;
 	struct switch_dev hpd_switch;
+	struct switch_dev hpd_audio_switch;
 
 	/* choose DVI or HDMI mode */
 	int dvi_mode;
+
+	struct mutex mutex;
+	int color_range;
 };
 
 struct hdmi_conf {
@@ -341,6 +354,7 @@
 int is_hdmiphy_ready(struct hdmi_device *hdev);
 void hdmi_enable(struct hdmi_device *hdev, int on);
 void hdmi_hpd_enable(struct hdmi_device *hdev, int on);
+void hdmi_hpd_clear_int(struct hdmi_device *hdev);
 void hdmi_tg_enable(struct hdmi_device *hdev, int on);
 void hdmi_reg_stop_vsi(struct hdmi_device *hdev);
 void hdmi_reg_infoframe(struct hdmi_device *hdev,
@@ -362,6 +376,7 @@
 void hdmi_dumpregs(struct hdmi_device *hdev, char *prefix);
 void hdmi_set_3d_info(struct hdmi_device *hdev);
 void hdmi_set_dvi_mode(struct hdmi_device *hdev);
+void hdmi_debugfs_init(struct hdmi_device *hdev);
 
 /** HDCP functions */
 irqreturn_t hdcp_irq_handler(struct hdmi_device *hdev);
@@ -371,6 +386,13 @@
 int hdcp_i2c_read(struct hdmi_device *hdev, u8 offset, int bytes, u8 *buf);
 int hdcp_i2c_write(struct hdmi_device *hdev, u8 offset, int bytes, u8 *buf);
 
+/** EDID functions */
+int edid_update(struct hdmi_device *hdev);
+u32 edid_enum_presets(struct hdmi_device *hdev, int index);
+u32 edid_preferred_preset(struct hdmi_device *hdev);
+bool edid_supports_hdmi(struct hdmi_device *hdev);
+int edid_max_audio_channels(struct hdmi_device *hdev);
+
 static inline
 void hdmi_write(struct hdmi_device *hdev, u32 reg_id, u32 value)
 {
diff --git a/drivers/media/video/exynos/tv/hdmi_drv.c b/drivers/media/video/exynos/tv/hdmi_drv.c
index edc8e33..ade018f 100644
--- a/drivers/media/video/exynos/tv/hdmi_drv.c
+++ b/drivers/media/video/exynos/tv/hdmi_drv.c
@@ -42,9 +42,6 @@
 MODULE_DESCRIPTION("Samsung HDMI");
 MODULE_LICENSE("GPL");
 
-/* default preset configured on probe */
-#define HDMI_DEFAULT_PRESET V4L2_DV_1080P60
-
 /* I2C module and id for HDMIPHY */
 static struct i2c_board_info hdmiphy_info = {
 	I2C_BOARD_INFO("hdmiphy", 0x38),
@@ -276,14 +273,16 @@
 #endif
 
 		disable_irq(hdev->ext_irq);
-		cancel_work_sync(&hdev->hpd_work_ext);
+		cancel_delayed_work_sync(&hdev->hpd_work_ext);
 
 		s5p_v4l2_int_src_hdmi_hpd();
 		hdmi_hpd_enable(hdev, 1);
+		hdmi_hpd_clear_int(hdev);
 		enable_irq(hdev->int_irq);
 
 		dev_info(hdev->dev, "HDMI interrupt changed to internal\n");
 	} else {
+		cancel_work_sync(&hdev->work);
 		hdmi_hpd_enable(hdev, 0);
 		disable_irq(hdev->int_irq);
 		cancel_work_sync(&hdev->hpd_work);
@@ -320,6 +319,35 @@
 	case V4L2_CID_TV_SET_ASPECT_RATIO:
 		hdev->aspect = ctrl->value;
 		break;
+	case V4L2_CID_TV_ENABLE_HDMI_AUDIO:
+		mutex_lock(&hdev->mutex);
+		hdev->audio_enable = !!ctrl->value;
+		if (is_hdmi_streaming(hdev)) {
+			hdmi_set_infoframe(hdev);
+
+			hdmi_audio_enable(hdev, hdev->audio_enable);
+		}
+		mutex_unlock(&hdev->mutex);
+		break;
+	case V4L2_CID_TV_SET_NUM_CHANNELS:
+		mutex_lock(&hdev->mutex);
+		if ((ctrl->value == 2) || (ctrl->value == 6) ||
+							(ctrl->value == 8)) {
+			hdev->audio_channel_count = ctrl->value;
+		} else {
+			dev_err(dev, "invalid channel count\n");
+			hdev->audio_channel_count = 2;
+		}
+		if (is_hdmi_streaming(hdev))
+			hdmi_set_infoframe(hdev);
+		mutex_unlock(&hdev->mutex);
+		break;
+	case V4L2_CID_TV_SET_COLOR_RANGE:
+		hdev->color_range = ctrl->value;
+		break;
+	case V4L2_CID_TV_HDCP_ENABLE:
+		hdev->hdcp_info.hdcp_enable = ctrl->value;
+		break;
 	default:
 		dev_err(dev, "invalid control id\n");
 		ret = -EINVAL;
@@ -333,12 +361,25 @@
 {
 	struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
 	struct device *dev = hdev->dev;
+	int ret = 0;
 
-	ctrl->value = switch_get_state(&hdev->hpd_switch);
-	dev_dbg(dev, "HDMI cable is %s\n", ctrl->value ?
-			"connected" : "disconnected");
+	switch (ctrl->id) {
+	case V4L2_CID_TV_HPD_STATUS:
+		ctrl->value = switch_get_state(&hdev->hpd_switch);
+		break;
+	case V4L2_CID_TV_GET_DVI_MODE:
+		ctrl->value = hdev->dvi_mode;
+		break;
+	case V4L2_CID_TV_MAX_AUDIO_CHANNELS:
+		ctrl->value = edid_max_audio_channels(hdev);
+		break;
+	default:
+		dev_err(dev, "invalid control id\n");
+		ret = -EINVAL;
+		break;
+	}
 
-	return 0;
+	return ret;
 }
 
 static int hdmi_s_dv_preset(struct v4l2_subdev *sd,
@@ -395,11 +436,15 @@
 }
 
 static int hdmi_enum_dv_presets(struct v4l2_subdev *sd,
-	struct v4l2_dv_enum_preset *preset)
+	struct v4l2_dv_enum_preset *enum_preset)
 {
-	if (preset->index >= hdmi_pre_cnt)
+	struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+	u32 preset = edid_enum_presets(hdev, enum_preset->index);
+
+	if (preset == V4L2_DV_INVALID)
 		return -EINVAL;
-	return v4l_fill_dv_preset_info(hdmi_conf[preset->index].preset, preset);
+
+	return v4l_fill_dv_preset_info(preset, enum_preset);
 }
 
 static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = {
@@ -680,33 +725,54 @@
 irqreturn_t hdmi_irq_handler_ext(int irq, void *dev_data)
 {
 	struct hdmi_device *hdev = dev_data;
-	queue_work(system_nrt_wq, &hdev->hpd_work_ext);
+	queue_delayed_work(system_nrt_wq, &hdev->hpd_work_ext, 0);
 
 	return IRQ_HANDLED;
 }
 
+static void hdmi_hpd_changed(struct hdmi_device *hdev, int state)
+{
+	u32 preset;
+	int ret;
+
+	if (state == switch_get_state(&hdev->hpd_switch))
+		return;
+
+	if (state) {
+		ret = edid_update(hdev);
+		if (ret == -ENODEV)
+			return;
+
+		preset = edid_preferred_preset(hdev);
+		if (preset == V4L2_DV_INVALID)
+			preset = HDMI_DEFAULT_PRESET;
+
+		hdev->dvi_mode = !edid_supports_hdmi(hdev);
+		hdev->cur_preset = preset;
+		hdev->cur_conf = hdmi_preset2conf(preset);
+	}
+
+	switch_set_state(&hdev->hpd_switch, state);
+	switch_set_state(&hdev->hpd_audio_switch, state ? !hdev->dvi_mode : 0);
+
+	dev_info(hdev->dev, "%s\n", state ? "plugged" : "unplugged");
+}
+
 static void hdmi_hpd_work_ext(struct work_struct *work)
 {
 	int state;
 	struct hdmi_device *hdev = container_of(work, struct hdmi_device,
-						hpd_work_ext);
-
+						hpd_work_ext.work);
 	state = s5p_v4l2_hpd_read_gpio();
-	switch_set_state(&hdev->hpd_switch, state);
-
-	dev_info(hdev->dev, "%s (ext)\n", state ? "plugged" : "unplugged");
+	hdmi_hpd_changed(hdev, state);
 }
 
 static void hdmi_hpd_work(struct work_struct *work)
 {
-	int state;
 	struct hdmi_device *hdev = container_of(work, struct hdmi_device,
 						hpd_work);
 
-	state = hdmi_hpd_status(hdev);
-	switch_set_state(&hdev->hpd_switch, state);
-
-	dev_info(hdev->dev, "%s (int)\n", state ? "plugged" : "unplugged");
+	hdmi_hpd_changed(hdev, 0);
 }
 
 static int __devinit hdmi_probe(struct platform_device *pdev)
@@ -769,7 +835,8 @@
 	hdmi_dev->int_irq = res->start;
 
 	INIT_WORK(&hdmi_dev->hpd_work, hdmi_hpd_work);
-	INIT_WORK(&hdmi_dev->hpd_work_ext, hdmi_hpd_work_ext);
+	INIT_DELAYED_WORK(&hdmi_dev->hpd_work_ext, hdmi_hpd_work_ext);
+	mutex_init(&hdmi_dev->mutex);
 
 	/* setting v4l2 name to prevent WARN_ON in v4l2_device_register */
 	strlcpy(hdmi_dev->v4l2_dev.name, dev_name(dev),
@@ -827,6 +894,8 @@
 
 	hdmi_dev->hpd_switch.name = "hdmi";
 	switch_dev_register(&hdmi_dev->hpd_switch);
+	hdmi_dev->hpd_audio_switch.name = "hdmi_audio";
+	switch_dev_register(&hdmi_dev->hpd_audio_switch);
 
 	ret = request_irq(hdmi_dev->int_irq, hdmi_irq_handler,
 			0, "hdmi-int", hdmi_dev);
@@ -844,18 +913,15 @@
 		goto fail_ext;
 	}
 
-	if (s5p_v4l2_hpd_read_gpio())
-		switch_set_state(&hdmi_dev->hpd_switch, 1);
-	else
-		switch_set_state(&hdmi_dev->hpd_switch, 0);
-
 	hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET;
 	/* FIXME: missing fail preset is not supported */
 	hdmi_dev->cur_conf = hdmi_preset2conf(hdmi_dev->cur_preset);
 
-	/* default audio configuration : enable audio */
-	hdmi_dev->audio_enable = 1;
+	/* default audio configuration : disable audio */
+	hdmi_dev->audio_enable = 0;
+	hdmi_dev->audio_channel_count = 2;
 	hdmi_dev->sample_rate = DEFAULT_SAMPLE_RATE;
+	hdmi_dev->color_range = 3;
 	hdmi_dev->bits_per_sample = DEFAULT_BITS_PER_SAMPLE;
 	hdmi_dev->audio_codec = DEFAULT_AUDIO_CODEC;
 
@@ -874,8 +940,13 @@
 	if (ret)
 		goto fail_irq;
 
+	queue_delayed_work(system_nrt_wq, &hdmi_dev->hpd_work_ext,
+					  msecs_to_jiffies(1000));
+
 	dev_info(dev, "probe sucessful\n");
 
+	hdmi_debugfs_init(hdmi_dev);
+
 	return 0;
 
 fail_irq:
@@ -913,6 +984,7 @@
 	free_irq(hdmi_dev->ext_irq, hdmi_dev);
 	free_irq(hdmi_dev->int_irq, hdmi_dev);
 	switch_dev_unregister(&hdmi_dev->hpd_switch);
+	switch_dev_unregister(&hdmi_dev->hpd_audio_switch);
 	iounmap(hdmi_dev->regs);
 	hdmi_resources_cleanup(hdmi_dev);
 	flush_workqueue(hdmi_dev->hdcp_wq);
diff --git a/drivers/media/video/exynos/tv/hdmi_edid.c b/drivers/media/video/exynos/tv/hdmi_edid.c
new file mode 100644
index 0000000..150216c
--- /dev/null
+++ b/drivers/media/video/exynos/tv/hdmi_edid.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/module.h>
+
+#include "hdmi.h"
+
+#define EDID_SEGMENT_ADDR	(0x60 >> 1)
+#define EDID_ADDR		(0xA0 >> 1)
+#define EDID_BLOCK_SIZE		128
+#define EDID_SEGMENT(x)		((x) >> 1)
+#define EDID_OFFSET(x)		(((x) & 1) * EDID_BLOCK_SIZE)
+#define EDID_EXTENSION_FLAG	0x7E
+
+static struct i2c_client *edid_client;
+
+static struct edid_preset {
+	u32 preset;
+	u16 xres;
+	u16 yres;
+	u16 refresh;
+	char *name;
+	bool supported;
+} edid_presets[] = {
+	{ V4L2_DV_480P59_94,  720,  480,  59, "480p@59.94"},
+	{ V4L2_DV_576P50,     720,  576,  50, "576p@50" },
+	{ V4L2_DV_720P24,     1280, 720,  24, "720p@24" },
+	{ V4L2_DV_720P25,     1280, 720,  25, "720p@25" },
+	{ V4L2_DV_720P30,     1280, 720,  30, "720p@30" },
+	{ V4L2_DV_720P50,     1280, 720,  50, "720p@50" },
+	{ V4L2_DV_720P59_94,  1280, 720,  59, "720p@59.94" },
+	{ V4L2_DV_720P60,     1280, 720,  60, "720p@60" },
+	{ V4L2_DV_1080P24,    1920, 1080, 24, "1080p@24" },
+	{ V4L2_DV_1080P25,    1920, 1080, 25, "1080p@25" },
+	{ V4L2_DV_1080P30,    1920, 1080, 30, "1080p@30" },
+	{ V4L2_DV_1080P50,    1920, 1080, 50, "1080p@50" },
+	{ V4L2_DV_1080P60,    1920, 1080, 60, "1080p@60" },
+};
+
+static u32 preferred_preset = HDMI_DEFAULT_PRESET;
+static u32 edid_misc;
+static int max_audio_channels;
+
+static int edid_i2c_read(struct hdmi_device *hdev, u8 segment, u8 offset,
+						   u8 *buf, size_t len)
+{
+	struct device *dev = hdev->dev;
+	struct i2c_client *i2c = edid_client;
+	int cnt = 0;
+	int ret;
+	struct i2c_msg msg[] = {
+		{
+			.addr = EDID_SEGMENT_ADDR,
+			.flags = segment ? 0 : I2C_M_IGNORE_NAK,
+			.len = 1,
+			.buf = &segment
+		},
+		{
+			.addr = EDID_ADDR,
+			.flags = 0,
+			.len = 1,
+			.buf = &offset
+		},
+		{
+			.addr = EDID_ADDR,
+			.flags = I2C_M_RD,
+			.len = len,
+			.buf = buf
+		}
+	};
+
+	if (!i2c)
+		return -ENODEV;
+
+	do {
+		ret = i2c_transfer(i2c->adapter, msg, ARRAY_SIZE(msg));
+		if (ret == ARRAY_SIZE(msg))
+			break;
+
+		dev_dbg(dev, "%s: can't read data, retry %d\n", __func__, cnt);
+		msleep(25);
+		cnt++;
+	} while (cnt < 5);
+
+	if (cnt == 5) {
+		dev_err(dev, "%s: can't read data, timeout\n", __func__);
+		return -ETIME;
+	}
+
+	return 0;
+}
+
+static int
+edid_read_block(struct hdmi_device *hdev, int block, u8 *buf, size_t len)
+{
+	struct device *dev = hdev->dev;
+	int ret, i;
+	u8 segment = EDID_SEGMENT(block);
+	u8 offset = EDID_OFFSET(block);
+	u8 sum = 0;
+
+	if (len < EDID_BLOCK_SIZE)
+		return -EINVAL;
+
+	ret = edid_i2c_read(hdev, segment, offset, buf, EDID_BLOCK_SIZE);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < EDID_BLOCK_SIZE; i++)
+		sum += buf[i];
+
+	if (sum) {
+		dev_err(dev, "%s: checksum error block=%d sum=%d\n", __func__,
+								  block, sum);
+		return -EPROTO;
+	}
+
+	return 0;
+}
+
+static int edid_read(struct hdmi_device *hdev, u8 **data)
+{
+	u8 block0[EDID_BLOCK_SIZE];
+	u8 *edid;
+	int block = 0;
+	int block_cnt, ret;
+
+	ret = edid_read_block(hdev, 0, block0, sizeof(block0));
+	if (ret)
+		return ret;
+
+	block_cnt = block0[EDID_EXTENSION_FLAG] + 1;
+
+	edid = kmalloc(block_cnt * EDID_BLOCK_SIZE, GFP_KERNEL);
+	if (!edid)
+		return -ENOMEM;
+
+	memcpy(edid, block0, sizeof(block0));
+
+	while (++block < block_cnt) {
+		ret = edid_read_block(hdev, block,
+				edid + block * EDID_BLOCK_SIZE,
+					       EDID_BLOCK_SIZE);
+		if (ret) {
+			kfree(edid);
+			return ret;
+		}
+	}
+
+	*data = edid;
+	return block_cnt;
+}
+
+static struct edid_preset *edid_find_preset(struct fb_videomode *mode)
+{
+	struct edid_preset *preset = edid_presets;
+	int i;
+
+	if (mode->vmode & FB_VMODE_INTERLACED)
+		return NULL;
+
+	for (i = 0; i < ARRAY_SIZE(edid_presets); i++, preset++) {
+		if (mode->refresh == preset->refresh &&
+		    mode->xres    == preset->xres &&
+		    mode->yres    == preset->yres) {
+			return preset;
+		}
+	}
+
+	return NULL;
+}
+
+static void edid_use_default_preset(void)
+{
+	int i;
+
+	preferred_preset = HDMI_DEFAULT_PRESET;
+	for (i = 0; i < ARRAY_SIZE(edid_presets); i++)
+		edid_presets[i].supported =
+				(edid_presets[i].preset == preferred_preset);
+	max_audio_channels = 2;
+}
+
+int edid_update(struct hdmi_device *hdev)
+{
+	struct fb_monspecs specs;
+	struct edid_preset *preset;
+	bool first = true;
+	u8 *edid = NULL;
+	int channels_max = 0;
+	int ret = 0;
+	int i;
+
+	edid_misc = 0;
+
+	ret = edid_read(hdev, &edid);
+	if (ret < 0)
+		goto out;
+
+	print_hex_dump_bytes("EDID: ", DUMP_PREFIX_OFFSET, edid,
+						ret * EDID_BLOCK_SIZE);
+
+	fb_edid_to_monspecs(edid, &specs);
+	for (i = 1; i < ret; i++)
+		fb_edid_add_monspecs(edid + i * EDID_BLOCK_SIZE, &specs);
+
+	preferred_preset = V4L2_DV_INVALID;
+	for (i = 0; i < ARRAY_SIZE(edid_presets); i++)
+		edid_presets[i].supported = false;
+
+	for (i = 0; i < specs.modedb_len; i++) {
+		preset = edid_find_preset(&specs.modedb[i]);
+		if (preset) {
+			pr_info("EDID: found %s", preset->name);
+			preset->supported = true;
+			if (first) {
+				preferred_preset = preset->preset;
+				first = false;
+			}
+		}
+	}
+
+	edid_misc = specs.misc;
+	pr_info("EDID: misc flags %08x", edid_misc);
+
+	for (i = 0; i < specs.audiodb_len; i++) {
+		if (specs.audiodb[i].format != FB_AUDIO_LPCM)
+			continue;
+		if (specs.audiodb[i].channel_count > channels_max)
+			channels_max = specs.audiodb[i].channel_count;
+	}
+
+	if (edid_misc & FB_MISC_HDMI) {
+		if (channels_max)
+			max_audio_channels = channels_max;
+		else
+			max_audio_channels = 2;
+	} else {
+		max_audio_channels = 0;
+	}
+	pr_info("EDID: Audio channels %d", max_audio_channels);
+
+out:
+	/* No supported preset found, use default */
+	if (first)
+		edid_use_default_preset();
+
+	kfree(edid);
+	return ret;
+}
+
+u32 edid_enum_presets(struct hdmi_device *hdev, int index)
+{
+	int i, j = 0;
+
+	for (i = 0; i < ARRAY_SIZE(edid_presets); i++) {
+		if (edid_presets[i].supported) {
+			if (j++ == index)
+				return edid_presets[i].preset;
+		}
+	}
+
+	return V4L2_DV_INVALID;
+}
+
+u32 edid_preferred_preset(struct hdmi_device *hdev)
+{
+	return preferred_preset;
+}
+
+bool edid_supports_hdmi(struct hdmi_device *hdev)
+{
+	return edid_misc & FB_MISC_HDMI;
+}
+
+int edid_max_audio_channels(struct hdmi_device *hdev)
+{
+	return max_audio_channels;
+}
+
+static int __devinit edid_probe(struct i2c_client *client,
+				const struct i2c_device_id *dev_id)
+{
+	edid_client = client;
+	edid_use_default_preset();
+	dev_info(&client->adapter->dev, "probed exynos edid\n");
+	return 0;
+}
+
+static int edid_remove(struct i2c_client *client)
+{
+	edid_client = NULL;
+	dev_info(&client->adapter->dev, "removed exynos edid\n");
+	return 0;
+}
+
+static struct i2c_device_id edid_idtable[] = {
+	{"exynos_edid", 0},
+};
+MODULE_DEVICE_TABLE(i2c, edid_idtable);
+
+static struct i2c_driver edid_driver = {
+	.driver = {
+		.name = "exynos_edid",
+		.owner = THIS_MODULE,
+	},
+	.id_table	= edid_idtable,
+	.probe		= edid_probe,
+	.remove		= __devexit_p(edid_remove),
+};
+
+static int __init edid_init(void)
+{
+	return i2c_add_driver(&edid_driver);
+}
+
+static void __exit edid_exit(void)
+{
+	i2c_del_driver(&edid_driver);
+}
+module_init(edid_init);
+module_exit(edid_exit);
diff --git a/drivers/media/video/exynos/tv/hdmi_reg_5250.c b/drivers/media/video/exynos/tv/hdmi_reg_5250.c
index 3120a7a..37e33d4 100644
--- a/drivers/media/video/exynos/tv/hdmi_reg_5250.c
+++ b/drivers/media/video/exynos/tv/hdmi_reg_5250.c
@@ -12,8 +12,10 @@
  */
 
 #include "hdmi.h"
+#include <linux/debugfs.h>
 #include <linux/delay.h>
 #include <linux/pm_runtime.h>
+#include <linux/seq_file.h>
 #include <plat/devs.h>
 #include <plat/tv-core.h>
 
@@ -2257,21 +2259,21 @@
 				hdcp_stop(hdev);
 			hdmi_write_mask(hdev, HDMI_INTC_FLAG_0, ~0,
 					HDMI_INTC_FLAG_HPD_UNPLUG);
+			queue_work(system_nrt_wq, &hdev->hpd_work);
 		}
 		if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) {
 			hdmi_write_mask(hdev, HDMI_INTC_FLAG_0, ~0,
 					HDMI_INTC_FLAG_HPD_PLUG);
 		}
+
 		if (intc_flag & HDMI_INTC_FLAG_HDCP) {
-			pr_info("hdcp interrupt occur\n");
+			pr_debug("%s: hdcp interrupt occur\n", __func__);
 			hdcp_irq_handler(hdev);
 			hdmi_write_mask(hdev, HDMI_INTC_FLAG_0, ~0,
 					HDMI_INTC_FLAG_HDCP);
 		}
 	}
 
-	queue_work(system_nrt_wq, &hdev->hpd_work);
-
 	return IRQ_HANDLED;
 }
 
@@ -2290,7 +2292,10 @@
 	/* RGB888 is default output format of HDMI,
 	 * look to CEA-861-D, table 7 for more detail */
 	hdmi_writeb(hdev, HDMI_AVI_BYTE(1), 0 << 5);
-	hdmi_write_mask(hdev, HDMI_CON_1, 2, 3 << 5);
+	if (hdev->color_range == 0 || hdev->color_range == 2)
+		hdmi_write_mask(hdev, HDMI_CON_1, 0, 3 << 5);
+	else
+		hdmi_write_mask(hdev, HDMI_CON_1, 1 << 5, 3 << 5);
 }
 
 void hdmi_set_dvi_mode(struct hdmi_device *hdev)
@@ -2476,9 +2481,19 @@
 
 void hdmi_hpd_enable(struct hdmi_device *hdev, int on)
 {
-	/* enable HPD interrupts */
-	hdmi_write_mask(hdev, HDMI_INTC_CON_0, ~0, HDMI_INTC_EN_GLOBAL |
-			HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
+	/* enable/disable HPD interrupts */
+	if (on)
+		hdmi_write_mask(hdev, HDMI_INTC_CON_0, ~0, HDMI_INTC_EN_GLOBAL |
+				HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
+	else
+		hdmi_write_mask(hdev, HDMI_INTC_CON_0, 0, HDMI_INTC_EN_GLOBAL |
+				HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
+}
+
+void hdmi_hpd_clear_int(struct hdmi_device *hdev)
+{
+	hdmi_write_mask(hdev, HDMI_INTC_FLAG_0, ~0,
+			HDMI_INTC_FLAG_HPD_PLUG | HDMI_INTC_FLAG_HPD_UNPLUG);
 }
 
 void hdmi_tg_enable(struct hdmi_device *hdev, int on)
@@ -2565,7 +2580,7 @@
 		hdmi_writeb(hdev, HDMI_AVI_HEADER2, infoframe->len);
 		hdr_sum = infoframe->type + infoframe->ver + infoframe->len;
 		hdmi_writeb(hdev, HDMI_AVI_BYTE(1), hdev->output_fmt << 5 |
-				AVI_ACTIVE_FORMAT_VALID);
+				AVI_ACTIVE_FORMAT_VALID | AVI_UNDERSCAN);
 		if (hdev->aspect == HDMI_ASPECT_RATIO_4_3 &&
 				(hdev->cur_preset == V4L2_DV_480P59_94 ||
 				 hdev->cur_preset == V4L2_DV_480P60)) {
@@ -2583,7 +2598,11 @@
 		}
 
 		hdmi_writeb(hdev, HDMI_AVI_BYTE(2), aspect_ratio |
-				AVI_SAME_AS_PIC_ASPECT_RATIO);
+				AVI_SAME_AS_PIC_ASPECT_RATIO | AVI_ITU709);
+		if (hdev->color_range == 0 || hdev->color_range == 2)
+			hdmi_writeb(hdev, HDMI_AVI_BYTE(3), AVI_FULL_RANGE);
+		else
+			hdmi_writeb(hdev, HDMI_AVI_BYTE(3), AVI_LIMITED_RANGE);
 		dev_dbg(dev, "VIC code = %d\n", vic);
 		hdmi_writeb(hdev, HDMI_AVI_BYTE(4), vic);
 		chksum = hdmi_chksum(hdev, HDMI_AVI_BYTE(1), infoframe->len, hdr_sum);
@@ -2596,6 +2615,13 @@
 		hdmi_writeb(hdev, HDMI_AUI_HEADER1, infoframe->ver);
 		hdmi_writeb(hdev, HDMI_AUI_HEADER2, infoframe->len);
 		hdr_sum = infoframe->type + infoframe->ver + infoframe->len;
+		/* speaker placement */
+		if (hdev->audio_channel_count == 6)
+			hdmi_writeb(hdev, HDMI_AUI_BYTE(4), 0x0b);
+		else if (hdev->audio_channel_count == 8)
+			hdmi_writeb(hdev, HDMI_AUI_BYTE(4), 0x13);
+		else
+			hdmi_writeb(hdev, HDMI_AUI_BYTE(4), 0x00);
 		chksum = hdmi_chksum(hdev, HDMI_AUI_BYTE(1), infoframe->len, hdr_sum);
 		dev_dbg(dev, "AUI checksum = 0x%x\n", chksum);
 		hdmi_writeb(hdev, HDMI_AUI_CHECK_SUM, chksum);
@@ -2751,7 +2777,20 @@
 		HDMI_I2S_CONSUMER_FORMAT;
 	hdmi_write(hdev, HDMI_I2S_CH_ST_0, val);
 	hdmi_write(hdev, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
-	hdmi_write(hdev, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
+	hdmi_writeb(hdev, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0) |
+			HDMI_I2S_SET_CHANNEL_NUM(0x6));
+	hdmi_writeb(hdev, HDMI_ASP_CON,
+			HDMI_AUD_MODE_MULTI_CH | HDMI_AUD_SP_AUD2_EN |
+			HDMI_AUD_SP_AUD1_EN | HDMI_AUD_SP_AUD0_EN);
+	hdmi_writeb(hdev, HDMI_ASP_CHCFG0,
+			HDMI_SPK0R_SEL_I_PCM0R | HDMI_SPK0L_SEL_I_PCM0L);
+	hdmi_writeb(hdev, HDMI_ASP_CHCFG1,
+			HDMI_SPK0R_SEL_I_PCM1L | HDMI_SPK0L_SEL_I_PCM1R);
+	hdmi_writeb(hdev, HDMI_ASP_CHCFG2,
+			HDMI_SPK0R_SEL_I_PCM2R | HDMI_SPK0L_SEL_I_PCM2L);
+	hdmi_writeb(hdev, HDMI_ASP_CHCFG3,
+			HDMI_SPK0R_SEL_I_PCM3R | HDMI_SPK0L_SEL_I_PCM3L);
+
 	val = HDMI_I2S_CLK_ACCUR_LEVEL_1 |
 		HDMI_I2S_SET_SAMPLING_FREQ(sample_frq);
 	hdmi_write(hdev, HDMI_I2S_CH_ST_3, val);
@@ -2778,7 +2817,7 @@
 void hdmi_audio_enable(struct hdmi_device *hdev, int on)
 {
 	if (on) {
-		if (hdev->dvi_mode)
+		if (hdev->dvi_mode || !hdev->audio_enable)
 			return;
 		hdmi_write_mask(hdev, HDMI_CON_0, ~0, HDMI_ASP_ENABLE);
 	} else
@@ -2806,7 +2845,7 @@
 
 int is_hdmi_streaming(struct hdmi_device *hdev)
 {
-	if (hdmi_hpd_status(hdev) && hdev->streaming)
+	if (hdev->streaming && hdmi_hpd_status(hdev))
 		return 1;
 	return 0;
 }
@@ -2856,6 +2895,89 @@
 	hdmi_write_mask(hdev, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT);
 }
 
+static int hdmi_debugfs_show(struct seq_file *s, void *unused)
+{
+	struct hdmi_device *hdev = s->private;
+	int i;
+
+	mutex_lock(&hdev->mutex);
+
+	if (!hdev->streaming) {
+		mutex_unlock(&hdev->mutex);
+		seq_printf(s, "Not streaming\n");
+		return 0;
+	}
+
+#define DUMPREG(reg_id) \
+		seq_printf(s, "%-20s %08x\n", #reg_id, \
+			   readl(hdev->regs + reg_id))
+
+	DUMPREG(HDMI_INTC_CON_0);
+	DUMPREG(HDMI_INTC_FLAG_0);
+	DUMPREG(HDMI_HPD_STATUS);
+	DUMPREG(HDMI_INTC_CON_1);
+	DUMPREG(HDMI_INTC_FLAG_1);
+	DUMPREG(HDMI_PHY_STATUS_0);
+	DUMPREG(HDMI_PHY_STATUS_PLL);
+	DUMPREG(HDMI_PHY_CON_0);
+	DUMPREG(HDMI_PHY_RSTOUT);
+	DUMPREG(HDMI_PHY_VPLL);
+	DUMPREG(HDMI_PHY_CMU);
+	DUMPREG(HDMI_CORE_RSTOUT);
+
+	DUMPREG(HDMI_CON_0);
+	DUMPREG(HDMI_CON_1);
+	DUMPREG(HDMI_CON_2);
+	DUMPREG(HDMI_STATUS);
+	DUMPREG(HDMI_PHY_STATUS);
+	DUMPREG(HDMI_STATUS_EN);
+	DUMPREG(HDMI_HPD);
+	DUMPREG(HDMI_MODE_SEL);
+	DUMPREG(HDMI_ENC_EN);
+	DUMPREG(HDMI_DC_CONTROL);
+	DUMPREG(HDMI_VIDEO_PATTERN_GEN);
+
+	DUMPREG(HDMI_AVI_CON);
+	DUMPREG(HDMI_AVI_HEADER0);
+	DUMPREG(HDMI_AVI_HEADER1);
+	DUMPREG(HDMI_AVI_HEADER2);
+	DUMPREG(HDMI_AVI_CHECK_SUM);
+	for (i = 1; i < 6; ++i)
+		DUMPREG(HDMI_AVI_BYTE(i));
+
+	DUMPREG(HDMI_VSI_CON);
+	DUMPREG(HDMI_VSI_HEADER0);
+	DUMPREG(HDMI_VSI_HEADER1);
+	DUMPREG(HDMI_VSI_HEADER2);
+	for (i = 0; i < 7; ++i)
+		DUMPREG(HDMI_VSI_DATA(i));
+	DUMPREG(HDMI_AUI_CON);
+	DUMPREG(HDMI_ACR_CON);
+
+#undef DUMPREG
+
+	mutex_unlock(&hdev->mutex);
+	return 0;
+}
+
+static int hdmi_debugfs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, hdmi_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations hdmi_debugfs_fops = {
+	.open           = hdmi_debugfs_open,
+	.read           = seq_read,
+	.llseek         = seq_lseek,
+	.release        = single_release,
+};
+
+void hdmi_debugfs_init(struct hdmi_device *hdev)
+{
+	debugfs_create_file(dev_name(hdev->dev), S_IRUGO, NULL,
+			    hdev, &hdmi_debugfs_fops);
+}
+
 void hdmi_dumpregs(struct hdmi_device *hdev, char *prefix)
 {
 #define DUMPREG(reg_id) \
diff --git a/drivers/media/video/exynos/tv/hdmiphy_conf_5250.c b/drivers/media/video/exynos/tv/hdmiphy_conf_5250.c
index 5cc0643..fcf7142 100644
--- a/drivers/media/video/exynos/tv/hdmiphy_conf_5250.c
+++ b/drivers/media/video/exynos/tv/hdmiphy_conf_5250.c
@@ -48,10 +48,10 @@
 };
 
 static const u8 hdmiphy_conf148_5[32] = {
-	0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
-	0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
-	0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
-	0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
+	0x01, 0xd1, 0x3e, 0x15, 0x40, 0x40, 0xf0, 0x08,
+	0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x20,
+	0xca, 0x80, 0x11, 0x07, 0x02, 0x22, 0x44, 0x87,
+	0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
 };
 
 const struct hdmiphy_conf hdmiphy_conf[] = {
diff --git a/drivers/media/video/exynos/tv/mixer.h b/drivers/media/video/exynos/tv/mixer.h
index ad30225..e980c90 100644
--- a/drivers/media/video/exynos/tv/mixer.h
+++ b/drivers/media/video/exynos/tv/mixer.h
@@ -120,6 +120,13 @@
 	MXR_GEOMETRY_SOURCE,
 };
 
+enum s5p_mixer_rgb {
+	MIXER_RGB601_0_255,
+	MIXER_RGB601_16_235,
+	MIXER_RGB709_0_255,
+	MIXER_RGB709_16_235
+};
+
 /** description of transformation from source to destination image */
 struct mxr_geometry {
 	/** cropping for source image */
@@ -141,6 +148,19 @@
 	struct list_head	wait;
 };
 
+struct mxr_layer_update {
+	bool			update;
+	struct mxr_buffer	*buffer;
+	const struct mxr_format	*fmt;
+	struct mxr_geometry	geo;
+};
+
+struct mxr_update {
+	struct work_struct       work;
+	struct mxr_device        *mdev;
+	struct mxr_layer_update  layers[MXR_MAX_LAYERS];
+};
+
 /** TV graphic layer pipeline state */
 enum mxr_pipeline_state {
 	/** graphic layer is not shown */
@@ -174,7 +194,8 @@
 	/** setting buffer to HW */
 	void (*buffer_set)(struct mxr_layer *, struct mxr_buffer *);
 	/** setting format and geometry in HW */
-	void (*format_set)(struct mxr_layer *);
+	void (*format_set)(struct mxr_layer *, const struct mxr_format *fmt,
+					       struct mxr_geometry *geo);
 	/** streaming stop/start */
 	void (*stream_set)(struct mxr_layer *, int);
 	/** adjusting geometry */
@@ -217,8 +238,6 @@
 
 	/** list for buffers waiting on a fence */
 	struct list_head fence_wait_list;
-	struct workqueue_struct *fence_wq;
-	struct work_struct fence_work;
 
 	/** buffer currently owned by hardware in temporary registers */
 	struct mxr_buffer *update_buf;
@@ -300,11 +319,6 @@
 	struct clk *sclk_hdmi;
 };
 
-/* event flags used  */
-enum mxr_devide_flags {
-	MXR_EVENT_VSYNC = 0,
-};
-
 /** videobuf2 context of mixer */
 struct mxr_vb2 {
 	const struct vb2_mem_ops *ops;
@@ -354,14 +368,16 @@
 	const struct mxr_vb2 *vb2;
 	/** context of allocator */
 	void *alloc_ctx;
-	/** event wait queue */
-	wait_queue_head_t event_queue;
-	/** state flags */
-	unsigned long event_flags;
+
+	/** vsync wait queue */
+	wait_queue_head_t vsync_wait;
+	ktime_t           vsync_timestamp;
 
 	/** spinlock for protection of registers */
 	spinlock_t reg_slock;
 
+	struct workqueue_struct *update_wq;
+
 	/** mutex for protection of fields below */
 	struct mutex mutex;
 	/** mutex for protection of streamer */
@@ -396,6 +412,8 @@
 
 	struct exynos5_bus_mif_handle *mif_handle;
 	struct exynos5_bus_int_handle *int_handle;
+
+	int color_range;
 };
 
 #if defined(CONFIG_VIDEOBUF2_CMA_PHYS)
@@ -520,8 +538,11 @@
 
 void mxr_layer_sync(struct mxr_device *mdev, int en);
 void mxr_vsync_set_update(struct mxr_device *mdev, int en);
+void mxr_vsync_enable_update(struct mxr_device *mdev);
+void mxr_vsync_disable_update(struct mxr_device *mdev);
 void mxr_reg_reset(struct mxr_device *mdev);
 void mxr_reg_set_layer_prio(struct mxr_device *mdev);
+void mxr_reg_set_color_range(struct mxr_device *mdev);
 void mxr_reg_set_layer_blend(struct mxr_device *mdev, int sub_mxr, int num,
 		int en);
 void mxr_reg_layer_alpha(struct mxr_device *mdev, int sub_mxr, int num, u32 a);
@@ -534,7 +555,7 @@
 void mxr_reg_s_output(struct mxr_device *mdev, int cookie);
 void mxr_reg_streamon(struct mxr_device *mdev);
 void mxr_reg_streamoff(struct mxr_device *mdev);
-int mxr_reg_wait4vsync(struct mxr_device *mdev);
+int mxr_reg_wait4update(struct mxr_device *mdev);
 void mxr_reg_set_mbus_fmt(struct mxr_device *mdev,
 	struct v4l2_mbus_framefmt *fmt, u32 dvi_mode);
 void mxr_reg_local_path_clear(struct mxr_device *mdev);
diff --git a/drivers/media/video/exynos/tv/mixer_drv.c b/drivers/media/video/exynos/tv/mixer_drv.c
index 9c9e46e..9108632 100644
--- a/drivers/media/video/exynos/tv/mixer_drv.c
+++ b/drivers/media/video/exynos/tv/mixer_drv.c
@@ -131,7 +131,8 @@
 				layer = sub_mxr->layer[MXR_LAYER_VIDEO];
 				layer->pipe.state = MXR_PIPELINE_STREAMING;
 				mxr_layer_geo_fix(layer);
-				layer->ops.format_set(layer);
+				layer->ops.format_set(layer, layer->fmt,
+							    &layer->geo);
 				layer->ops.stream_set(layer, 1);
 				local += sub_mxr->local;
 			}
@@ -147,6 +148,7 @@
 	/* Alpha blending configuration always can be changed
 	 * whenever streaming */
 	mxr_set_alpha_blend(mdev);
+	mxr_reg_set_color_range(mdev);
 	mxr_reg_set_layer_prio(mdev);
 
 	if ((mdev->n_streamer == 1 && local == 1) ||
@@ -210,7 +212,7 @@
 			goto out;
 		}
 
-		ret = mxr_reg_wait4vsync(mdev);
+		ret = mxr_reg_wait4update(mdev);
 		if (ret) {
 			mxr_err(mdev, "failed to get vsync (%d) from output\n",
 					ret);
@@ -275,7 +277,7 @@
 
 		mxr_reg_streamoff(mdev);
 		/* vsync applies Mixer setup */
-		ret = mxr_reg_wait4vsync(mdev);
+		ret = mxr_reg_wait4update(mdev);
 		if (ret) {
 			mxr_err(mdev, "failed to get vsync (%d) from output\n",
 					ret);
@@ -1392,6 +1394,7 @@
 
 	/* setup pointer to master device */
 	mdev->dev = dev;
+	mdev->color_range = 3;
 
 	/* use only sub mixer0 as default */
 	mdev->sub_mxr[MXR_SUB_MIXER0].use = 1;
@@ -1406,7 +1409,14 @@
 	mutex_init(&mdev->mutex);
 	mutex_init(&mdev->s_mutex);
 	spin_lock_init(&mdev->reg_slock);
-	init_waitqueue_head(&mdev->event_queue);
+	init_waitqueue_head(&mdev->vsync_wait);
+
+	mdev->update_wq = create_singlethread_workqueue("hdmi-mixer");
+	if (mdev->update_wq == NULL) {
+		ret = -ENOMEM;
+		mxr_err(mdev, "failed to create work queue\n");
+		goto fail_mem;
+	}
 
 	/* acquire resources: regs, irqs, clocks, regulators */
 	ret = mxr_acquire_resources(mdev, pdev);
@@ -1455,6 +1465,8 @@
 	mxr_release_resources(mdev);
 
 fail_mem:
+	if (mdev->update_wq)
+		destroy_workqueue(mdev->update_wq);
 	kfree(mdev);
 
 fail:
@@ -1469,6 +1481,7 @@
 
 	pm_runtime_disable(dev);
 
+	destroy_workqueue(mdev->update_wq);
 	mxr_release_layers(mdev);
 	mxr_release_video(mdev);
 	mxr_release_resources(mdev);
diff --git a/drivers/media/video/exynos/tv/mixer_grp_layer.c b/drivers/media/video/exynos/tv/mixer_grp_layer.c
index 447e0e3..973aa09 100644
--- a/drivers/media/video/exynos/tv/mixer_grp_layer.c
+++ b/drivers/media/video/exynos/tv/mixer_grp_layer.c
@@ -100,10 +100,11 @@
 	mxr_reg_graph_layer_stream(layer->mdev, layer->idx, en);
 }
 
-static void mxr_graph_format_set(struct mxr_layer *layer)
+static void mxr_graph_format_set(struct mxr_layer *layer,
+				 const struct mxr_format *fmt,
+				 struct mxr_geometry *geo)
 {
-	mxr_reg_graph_format(layer->mdev, layer->idx,
-			layer->fmt, &layer->geo);
+	mxr_reg_graph_format(layer->mdev, layer->idx, fmt, geo);
 }
 
 static void mxr_graph_fix_geometry(struct mxr_layer *layer)
diff --git a/drivers/media/video/exynos/tv/mixer_reg.c b/drivers/media/video/exynos/tv/mixer_reg.c
index 37c10bd..a959369 100644
--- a/drivers/media/video/exynos/tv/mixer_reg.c
+++ b/drivers/media/video/exynos/tv/mixer_reg.c
@@ -75,6 +75,17 @@
 		MXR_STATUS_LAYER_SYNC);
 }
 
+void mxr_vsync_enable_update(struct mxr_device *mdev)
+{
+	mxr_write_mask(mdev, MXR_CFG,    ~0, MXR_CFG_LAYER_UPDATE);
+	mxr_write_mask(mdev, MXR_STATUS, ~0, MXR_STATUS_SYNC_ENABLE);
+}
+
+void mxr_vsync_disable_update(struct mxr_device *mdev)
+{
+	mxr_write_mask(mdev, MXR_STATUS, 0, MXR_STATUS_SYNC_ENABLE);
+}
+
 void mxr_vsync_set_update(struct mxr_device *mdev, int en)
 {
 	/* block update on vsync */
@@ -317,10 +328,6 @@
 void mxr_reg_graph_buffer(struct mxr_device *mdev, int idx, dma_addr_t addr)
 {
 	u32 val = addr ? ~0 : 0;
-	unsigned long flags;
-
-	spin_lock_irqsave(&mdev->reg_slock, flags);
-	mxr_vsync_set_update(mdev, MXR_DISABLE);
 
 	if (idx == 0) {
 		mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
@@ -335,9 +342,6 @@
 		mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_MX1_GRP1_ENABLE);
 		mxr_write(mdev, MXR1_GRAPHIC_BASE(1), addr);
 	}
-
-	mxr_vsync_set_update(mdev, MXR_ENABLE);
-	spin_unlock_irqrestore(&mdev->reg_slock, flags);
 }
 
 void mxr_reg_vp_buffer(struct mxr_device *mdev,
@@ -458,17 +462,21 @@
 
 	if (sub_mxr == MXR_SUB_MIXER0 && num == MXR_LAYER_GRP0)
 		mxr_write_mask(mdev, MXR_GRAPHIC_CFG(0), val,
-				MXR_GRP_CFG_PIXEL_BLEND_EN);
+				MXR_GRP_CFG_PIXEL_BLEND_EN |
+				MXR_GRP_CFG_PRE_MUL_MODE);
 	else if (sub_mxr == MXR_SUB_MIXER0 && num == MXR_LAYER_GRP1)
 		mxr_write_mask(mdev, MXR_GRAPHIC_CFG(1), val,
-				MXR_GRP_CFG_PIXEL_BLEND_EN);
+				MXR_GRP_CFG_PIXEL_BLEND_EN |
+				MXR_GRP_CFG_PRE_MUL_MODE);
 #if defined(CONFIG_ARCH_EXYNOS5)
 	else if (sub_mxr == MXR_SUB_MIXER1 && num == MXR_LAYER_GRP0)
 		mxr_write_mask(mdev, MXR1_GRAPHIC_CFG(0), val,
-				MXR_GRP_CFG_PIXEL_BLEND_EN);
+				MXR_GRP_CFG_PIXEL_BLEND_EN |
+				MXR_GRP_CFG_PRE_MUL_MODE);
 	else if (sub_mxr == MXR_SUB_MIXER1 && num == MXR_LAYER_GRP1)
 		mxr_write_mask(mdev, MXR1_GRAPHIC_CFG(1), val,
-				MXR_GRP_CFG_PIXEL_BLEND_EN);
+				MXR_GRP_CFG_PIXEL_BLEND_EN |
+				MXR_GRP_CFG_PRE_MUL_MODE);
 #endif
 
 	mxr_vsync_set_update(mdev, MXR_ENABLE);
@@ -524,42 +532,6 @@
 	spin_unlock_irqrestore(&mdev->reg_slock, flags);
 }
 
-static void mxr_irq_layer_handle(struct mxr_layer *layer)
-{
-	struct list_head *head = &layer->enq_list;
-	struct mxr_pipeline *pipe = &layer->pipe;
-	struct mxr_buffer *done;
-
-	/* skip non-existing layer */
-	if (layer == NULL)
-		return;
-
-	spin_lock(&layer->enq_slock);
-	if (pipe->state == MXR_PIPELINE_IDLE)
-		goto done;
-
-	done = layer->shadow_buf;
-	layer->shadow_buf = layer->update_buf;
-
-	if (list_empty(head)) {
-		if (pipe->state != MXR_PIPELINE_STREAMING)
-			layer->update_buf = NULL;
-	} else {
-		struct mxr_buffer *next;
-		next = list_first_entry(head, struct mxr_buffer, list);
-		list_del(&next->list);
-		layer->update_buf = next;
-	}
-
-	layer->ops.buffer_set(layer, layer->update_buf);
-
-	if (done && done != layer->shadow_buf)
-		vb2_buffer_done(&done->vb, VB2_BUF_STATE_DONE);
-
-done:
-	spin_unlock(&layer->enq_slock);
-}
-
 u32 mxr_irq_underrun_handle(struct mxr_device *mdev, u32 val)
 {
 	if (val & MXR_INT_STATUS_MX0_VIDEO) {
@@ -588,15 +560,15 @@
 irqreturn_t mxr_irq_handler(int irq, void *dev_data)
 {
 	struct mxr_device *mdev = dev_data;
-	u32 i, val;
+	u32 val;
 
 	spin_lock(&mdev->reg_slock);
 	val = mxr_read(mdev, MXR_INT_STATUS);
 
 	/* wake up process waiting for VSYNC */
 	if (val & MXR_INT_STATUS_VSYNC) {
-		set_bit(MXR_EVENT_VSYNC, &mdev->event_flags);
-		wake_up(&mdev->event_queue);
+		mdev->vsync_timestamp = ktime_get();
+		wake_up_interruptible_all(&mdev->vsync_wait);
 	}
 
 	/* clear interrupts.
@@ -608,24 +580,6 @@
 	mxr_write(mdev, MXR_INT_STATUS, val);
 
 	spin_unlock(&mdev->reg_slock);
-	/* leave on non-vsync event */
-	if (~val & MXR_INT_CLEAR_VSYNC)
-		return IRQ_HANDLED;
-
-	for (i = 0; i < MXR_MAX_SUB_MIXERS; ++i) {
-#if defined(CONFIG_ARCH_EXYNOS4)
-		mxr_irq_layer_handle(mdev->sub_mxr[i].layer[MXR_LAYER_VIDEO]);
-#endif
-		mxr_irq_layer_handle(mdev->sub_mxr[i].layer[MXR_LAYER_GRP0]);
-		mxr_irq_layer_handle(mdev->sub_mxr[i].layer[MXR_LAYER_GRP1]);
-	}
-
-	if (test_bit(MXR_EVENT_VSYNC, &mdev->event_flags)) {
-		spin_lock(&mdev->reg_slock);
-		mxr_write_mask(mdev, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
-		spin_unlock(&mdev->reg_slock);
-	}
-
 	return IRQ_HANDLED;
 }
 
@@ -663,15 +617,20 @@
 	spin_unlock_irqrestore(&mdev->reg_slock, flags);
 }
 
-int mxr_reg_wait4vsync(struct mxr_device *mdev)
+static int mxr_update_pending(struct mxr_device *mdev)
 {
+	return MXR_CFG_LAYER_UPDATE_COUNT(mxr_read(mdev, MXR_CFG));
+}
+
+int mxr_reg_wait4update(struct mxr_device *mdev)
+{
+	ktime_t timestamp = mdev->vsync_timestamp;
 	int ret;
 
-	clear_bit(MXR_EVENT_VSYNC, &mdev->event_flags);
-	/* TODO: consider adding interruptible */
-	ret = wait_event_timeout(mdev->event_queue,
-		test_bit(MXR_EVENT_VSYNC, &mdev->event_flags),
-		msecs_to_jiffies(1000));
+	ret = wait_event_interruptible_timeout(mdev->vsync_wait,
+		!ktime_equal(timestamp, mdev->vsync_timestamp)
+				&& !mxr_update_pending(mdev),
+		msecs_to_jiffies(100));
 	if (ret > 0)
 		return 0;
 	if (ret < 0)
@@ -733,6 +692,19 @@
 	spin_unlock_irqrestore(&mdev->reg_slock, flags);
 }
 
+void mxr_reg_set_color_range(struct mxr_device *mdev)
+{
+	mxr_write(mdev, MXR_CM_COEFF_Y,
+			(1 << 30) | (94 << 20) | (314 << 10) | (32 << 0));
+	mxr_write(mdev, MXR_CM_COEFF_CB,
+			(972 << 20) | (851 << 10) | (225 << 0));
+	mxr_write(mdev, MXR_CM_COEFF_CR,
+			(225 << 20) | (820 << 10) | (1004 << 0));
+
+	mxr_write_mask(mdev, MXR_CFG, mdev->color_range << 9,
+				      MXR_CFG_COLOR_RANGE_MASK);
+}
+
 void mxr_reg_local_path_clear(struct mxr_device *mdev)
 {
 	u32 val;
diff --git a/drivers/media/video/exynos/tv/mixer_video.c b/drivers/media/video/exynos/tv/mixer_video.c
index 4f69d48..9e6575a 100644
--- a/drivers/media/video/exynos/tv/mixer_video.c
+++ b/drivers/media/video/exynos/tv/mixer_video.c
@@ -27,6 +27,8 @@
 #include <media/videobuf2-ion.h>
 #endif
 
+static int mxr_update(struct mxr_device *mdev);
+
 int __devinit mxr_acquire_video(struct mxr_device *mdev,
 	struct mxr_output_conf *output_conf, int output_count)
 {
@@ -423,20 +425,27 @@
 	case V4L2_CID_TV_CHROMA_VALUE:
 		layer->chroma_val = (u32)v;
 		break;
-	case V4L2_CID_TV_HPD_STATUS:
-		v4l2_subdev_call(to_outsd(mdev), core, s_ctrl, ctrl);
-		break;
-	case V4L2_CID_TV_SET_DVI_MODE:
-		v4l2_subdev_call(to_outsd(mdev), core, s_ctrl, ctrl);
-		break;
-	case V4L2_CID_TV_SET_ASPECT_RATIO:
-		v4l2_subdev_call(to_outsd(mdev), core, s_ctrl, ctrl);
 	case V4L2_CID_TV_LAYER_PRIO:
 		layer->prio = (u8)v;
 		/* This can be turned on/off each layer while streaming */
 		if (layer->pipe.state == MXR_PIPELINE_STREAMING)
 			mxr_reg_set_layer_prio(mdev);
 		break;
+	case V4L2_CID_TV_SET_COLOR_RANGE:
+		mdev->color_range = v;
+		v4l2_subdev_call(to_outsd(mdev), core, s_ctrl, ctrl);
+		break;
+	case V4L2_CID_TV_UPDATE:
+		ret = mxr_update(mdev);
+		break;
+	case V4L2_CID_TV_ENABLE_HDMI_AUDIO:
+	case V4L2_CID_TV_SET_NUM_CHANNELS:
+	case V4L2_CID_TV_HPD_STATUS:
+	case V4L2_CID_TV_SET_DVI_MODE:
+	case V4L2_CID_TV_SET_ASPECT_RATIO:
+	case V4L2_CID_TV_HDCP_ENABLE:
+		v4l2_subdev_call(to_outsd(mdev), core, s_ctrl, ctrl);
+		break;
 	default:
 		mxr_err(mdev, "invalid control id\n");
 		ret = -EINVAL;
@@ -466,6 +475,7 @@
 
 	switch (ctrl->id) {
 	case V4L2_CID_TV_HPD_STATUS:
+	case V4L2_CID_TV_MAX_AUDIO_CHANNELS:
 		v4l2_subdev_call(to_outsd(mdev), core, g_ctrl, ctrl);
 		break;
 	default:
@@ -885,44 +895,148 @@
 	return 0;
 }
 
-static void fence_work(struct work_struct *work)
+static void layer_update(struct mxr_layer *layer,
+			 struct mxr_layer_update *update)
 {
-	struct mxr_layer *layer = container_of(work, struct mxr_layer, fence_work);
+	layer->shadow_buf = layer->update_buf;
+	layer->update_buf = update->buffer;
+
+	if (layer->update_buf)
+		layer->ops.format_set(layer, update->fmt, &update->geo);
+
+	layer->ops.buffer_set(layer, layer->update_buf);
+}
+
+static void layer_buffer_done(struct mxr_layer *layer)
+{
 	struct mxr_pipeline *pipe = &layer->pipe;
-	struct mxr_buffer *buffer;
-	struct sync_fence *fence;
 	unsigned long flags;
-	int ret;
 
 	spin_lock_irqsave(&layer->enq_slock, flags);
 
-	while (!list_empty(&layer->fence_wait_list)) {
-		buffer = list_first_entry(&layer->fence_wait_list,
-					  struct mxr_buffer, wait);
-		list_del(&buffer->wait);
+	if (layer->shadow_buf && layer->shadow_buf != layer->update_buf)
+		vb2_buffer_done(&layer->shadow_buf->vb, VB2_BUF_STATE_DONE);
 
-		fence = buffer->vb.acquire_fence;
-		if (fence) {
-			buffer->vb.acquire_fence = NULL;
-			spin_unlock_irqrestore(&layer->enq_slock, flags);
-
-			ret = sync_fence_wait(fence, 100);
-			if (ret)
-				mxr_err(layer->mdev, "sync_fence_wait() timeout");
-			sync_fence_put(fence);
-
-			spin_lock_irqsave(&layer->enq_slock, flags);
-		}
-
-		list_add_tail(&buffer->list, &layer->enq_list);
-	}
-
-	if (pipe->state == MXR_PIPELINE_STREAMING_START)
+	if (layer->update_buf && pipe->state == MXR_PIPELINE_STREAMING_START)
 		pipe->state = MXR_PIPELINE_STREAMING;
 
+	if (!layer->update_buf && pipe->state == MXR_PIPELINE_STREAMING_FINISH)
+		pipe->state = MXR_PIPELINE_IDLE;
+
 	spin_unlock_irqrestore(&layer->enq_slock, flags);
 }
 
+static int buffer_fence_wait(struct mxr_device *mdev, struct mxr_buffer *buffer)
+{
+	struct sync_fence *fence;
+	int ret = 0;
+
+	fence = buffer->vb.acquire_fence;
+	if (!fence)
+		return 0;
+
+	ret = sync_fence_wait(fence, 1000);
+	if (ret == -ETIME) {
+		mxr_warn(mdev, "sync_fence_wait timeout");
+		ret = sync_fence_wait(fence, 10 * MSEC_PER_SEC);
+	}
+	if (ret)
+		mxr_warn(mdev, "sync_fence_wait error");
+
+	buffer->vb.acquire_fence = NULL;
+	sync_fence_put(fence);
+	return ret;
+}
+
+static void mxr_update_work(struct work_struct *work)
+{
+	struct mxr_update *update = container_of(work, struct mxr_update, work);
+	struct mxr_device *mdev = update->mdev;
+	struct mxr_layer **layers = mdev->sub_mxr[MXR_SUB_MIXER0].layer;
+	struct mxr_buffer *buffer;
+	int i;
+
+	mutex_lock(&mdev->s_mutex);
+
+	for (i = 0; i < MXR_MAX_LAYERS; i++) {
+		buffer = update->layers[i].buffer;
+		if (buffer)
+			buffer_fence_wait(mdev, buffer);
+	}
+
+	if (!mdev->n_streamer)
+		goto out;
+
+	mxr_vsync_disable_update(mdev);
+
+	for (i = 0; i < MXR_MAX_LAYERS; i++) {
+		if (update->layers[i].update)
+			layer_update(layers[i], &update->layers[i]);
+	}
+
+	mxr_vsync_enable_update(mdev);
+
+	mxr_reg_wait4update(mdev);
+
+	for (i = 0; i < ARRAY_SIZE(update->layers); i++)
+		if (update->layers[i].update)
+			layer_buffer_done(layers[i]);
+
+out:
+	mutex_unlock(&mdev->s_mutex);
+	kfree(update);
+}
+
+static void mxr_fill_update(struct mxr_layer *layer,
+			    struct mxr_layer_update *update)
+{
+	struct mxr_pipeline *pipe = &layer->pipe;
+	unsigned long flags;
+
+	spin_lock_irqsave(&layer->enq_slock, flags);
+
+	if ((pipe->state == MXR_PIPELINE_STREAMING && layer->prio == 0)
+			|| pipe->state == MXR_PIPELINE_STREAMING_FINISH) {
+		update->update = true;
+		update->buffer = NULL;
+	} else if (!list_empty(&layer->fence_wait_list)) {
+		update->update = true;
+		update->buffer = list_first_entry(&layer->fence_wait_list,
+					  struct mxr_buffer, wait);
+		list_del(&update->buffer->wait);
+	} else {
+		update->update = false;
+	}
+
+	spin_unlock_irqrestore(&layer->enq_slock, flags);
+
+	if (update->buffer) {
+		update->fmt = layer->fmt;
+		memcpy(&update->geo, &layer->geo, sizeof(layer->geo));
+	}
+}
+
+static int mxr_update(struct mxr_device *mdev)
+{
+	struct mxr_layer **layers = mdev->sub_mxr[MXR_SUB_MIXER0].layer;
+	struct mxr_update *update;
+	int i;
+
+	update = kzalloc(sizeof(struct mxr_update), GFP_KERNEL);
+	if (!update)
+		return -ENOMEM;
+
+	/* start from 1, only the graphic layers are supported */
+	for (i = 1; i < ARRAY_SIZE(update->layers); i++)
+		mxr_fill_update(layers[i], &update->layers[i]);
+
+	update->mdev = mdev;
+	INIT_WORK(&update->work, mxr_update_work);
+
+	queue_work(mdev->update_wq, &update->work);
+	return 0;
+}
+
 static void buf_queue(struct vb2_buffer *vb)
 {
 	struct mxr_buffer *buffer = container_of(vb, struct mxr_buffer, vb);
@@ -932,8 +1046,6 @@
 	spin_lock_irqsave(&layer->enq_slock, flags);
 	list_add_tail(&buffer->wait, &layer->fence_wait_list);
 	spin_unlock_irqrestore(&layer->enq_slock, flags);
-
-	queue_work(layer->fence_wq, &layer->fence_work);
 }
 
 static void wait_lock(struct vb2_queue *vq)
@@ -1021,8 +1133,6 @@
 	mxr_layer_geo_fix(layer);
 	mxr_geometry_dump(mdev, &layer->geo);
 
-	layer->ops.format_set(layer);
-
 	spin_lock_irqsave(&layer->enq_slock, flags);
 	if (list_empty(&layer->enq_list))
 		pipe->state = MXR_PIPELINE_STREAMING_START;
@@ -1040,42 +1150,16 @@
 	return 0;
 }
 
-static void mxr_watchdog(unsigned long arg)
-{
-	struct mxr_layer *layer = (struct mxr_layer *) arg;
-	struct mxr_device *mdev = layer->mdev;
-	unsigned long flags;
-
-	mxr_err(mdev, "watchdog fired for layer %s\n", layer->vfd.name);
-
-	spin_lock_irqsave(&layer->enq_slock, flags);
-
-	if (layer->update_buf == layer->shadow_buf)
-		layer->update_buf = NULL;
-	if (layer->update_buf) {
-		vb2_buffer_done(&layer->update_buf->vb, VB2_BUF_STATE_ERROR);
-		layer->update_buf = NULL;
-	}
-	if (layer->shadow_buf) {
-		vb2_buffer_done(&layer->shadow_buf->vb, VB2_BUF_STATE_ERROR);
-		layer->shadow_buf = NULL;
-	}
-	spin_unlock_irqrestore(&layer->enq_slock, flags);
-}
-
 static int stop_streaming(struct vb2_queue *vq)
 {
 	struct mxr_layer *layer = vb2_get_drv_priv(vq);
 	struct mxr_device *mdev = layer->mdev;
 	unsigned long flags;
-	struct timer_list watchdog;
 	struct mxr_buffer *buf, *buf_tmp;
 	struct mxr_pipeline *pipe = &layer->pipe;
 
 	mxr_dbg(mdev, "%s\n", __func__);
 
-	cancel_work_sync(&layer->fence_work);
-
 	spin_lock_irqsave(&layer->enq_slock, flags);
 
 	list_for_each_entry_safe(buf, buf_tmp, &layer->fence_wait_list, wait) {
@@ -1104,24 +1188,12 @@
 
 	spin_unlock_irqrestore(&layer->enq_slock, flags);
 
-	/* give 1 seconds to complete to complete last buffers */
-	setup_timer_on_stack(&watchdog, mxr_watchdog,
-		(unsigned long)layer);
-	mod_timer(&watchdog, jiffies + msecs_to_jiffies(1000));
+	/* to complete last buffer */
+	mxr_update(mdev);
 
 	/* wait until all buffers are goes to done state */
 	vb2_wait_for_all_buffers(vq);
 
-	/* stop timer if all synchronization is done */
-	del_timer_sync(&watchdog);
-	destroy_timer_on_stack(&watchdog);
-
-	/* stopping hardware */
-	spin_lock_irqsave(&layer->enq_slock, flags);
-
-	pipe->state = MXR_PIPELINE_IDLE;
-	spin_unlock_irqrestore(&layer->enq_slock, flags);
-
 	/* disabling layer in hardware */
 	layer->ops.stream_set(layer, MXR_DISABLE);
 
@@ -1216,13 +1288,7 @@
 	INIT_LIST_HEAD(&layer->enq_list);
 	INIT_LIST_HEAD(&layer->fence_wait_list);
 	mutex_init(&layer->mutex);
-	INIT_WORK(&layer->fence_work, fence_work);
 
-	layer->fence_wq = create_singlethread_workqueue(name);
-	if (layer->fence_wq == NULL) {
-		mxr_err(mdev, "failed to create work queue\n");
-		goto fail_alloc;
-	}
 
 	layer->vfd = (struct video_device) {
 		.minor = -1,
@@ -1261,8 +1327,6 @@
 	return layer;
 
 fail_alloc:
-	if (layer->fence_wq)
-		destroy_workqueue(layer->fence_wq);
 	kfree(layer);
 
 fail:
diff --git a/drivers/media/video/exynos/tv/mixer_video_layer.c b/drivers/media/video/exynos/tv/mixer_video_layer.c
index f2f8921..d94ea07 100644
--- a/drivers/media/video/exynos/tv/mixer_video_layer.c
+++ b/drivers/media/video/exynos/tv/mixer_video_layer.c
@@ -31,9 +31,11 @@
 	mxr_reg_video_layer_stream(layer->mdev, layer->idx, en);
 }
 
-static void mxr_video_format_set(struct mxr_layer *layer)
+static void mxr_video_format_set(struct mxr_layer *layer,
+				 const struct mxr_format *fmt,
+				 struct mxr_geometry *geo)
 {
-	mxr_reg_video_geo(layer->mdev, layer->cur_mxr, layer->idx, &layer->geo);
+	mxr_reg_video_geo(layer->mdev, layer->cur_mxr, layer->idx, geo);
 }
 
 static void mxr_video_fix_geometry(struct mxr_layer *layer)
diff --git a/drivers/media/video/exynos/tv/regs-hdmi-5250.h b/drivers/media/video/exynos/tv/regs-hdmi-5250.h
index 140ec96..3101fb7 100644
--- a/drivers/media/video/exynos/tv/regs-hdmi-5250.h
+++ b/drivers/media/video/exynos/tv/regs-hdmi-5250.h
@@ -1069,7 +1069,7 @@
 /* I2S_CH_ST_2 / I2S_CH_ST_SH_2 */
 #define HDMI_I2S_CHANNEL_NUM_MASK		(0xF << 4)
 #define HDMI_I2S_SOURCE_NUM_MASK		(0xF)
-#define HDMI_I2S_SET_CHANNEL_NUM(x)		((x) & (0xF) << 4)
+#define HDMI_I2S_SET_CHANNEL_NUM(x)		(((x) & (0xF)) << 4)
 #define HDMI_I2S_SET_SOURCE_NUM(x)		((x) & (0xF))
 
 /* I2S_CH_ST_3 / I2S_CH_ST_SH_3 */
diff --git a/drivers/media/video/exynos/tv/regs-mixer.h b/drivers/media/video/exynos/tv/regs-mixer.h
index 15ad119..59a7eb4 100644
--- a/drivers/media/video/exynos/tv/regs-mixer.h
+++ b/drivers/media/video/exynos/tv/regs-mixer.h
@@ -129,10 +129,11 @@
 
 /* bits for MXR_CFG */
 #define MXR_CFG_LAYER_UPDATE            (1 << 31)
-#define MXR_CFG_LAYER_UPDATE_COUNTER    (3 << 29)
+#define MXR_CFG_LAYER_UPDATE_COUNT(x)   (((x) >> 29) & 3)
 #define MXR_CFG_MX1_GRP1_ENABLE		(1 << 15)
 #define MXR_CFG_MX1_GRP0_ENABLE		(1 << 14)
 #define MXR_CFG_MX1_VIDEO_ENABLE	(1 << 13)
+#define MXR_CFG_COLOR_RANGE_MASK	(3 << 9)
 #define MXR_CFG_OUT_YUV444		(0 << 8)
 #define MXR_CFG_OUT_RGB888		(1 << 8)
 #define MXR_CFG_OUT_MASK		(1 << 8)
@@ -154,6 +155,7 @@
 
 /* bits for MXR_GRAPHICn_CFG */
 #define MXR_GRP_CFG_BLANK_KEY_OFF	(1 << 21)
+#define MXR_GRP_CFG_PRE_MUL_MODE	(1 << 20)
 #define MXR_GRP_CFG_LAYER_BLEND_EN	(1 << 17)
 #define MXR_GRP_CFG_PIXEL_BLEND_EN	(1 << 16)
 #define MXR_GRP_CFG_FORMAT_VAL(x)	MXR_MASK_VAL(x, 11, 8)
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 11e44386..db0e395 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -441,6 +441,17 @@
 	  additional drivers must be enabled in order to use the functionality
 	  of the device.
 
+config MFD_MAX77686
+        bool "Maxim Semiconductor MAX77686 PMIC Support"
+        depends on I2C=y && GENERIC_HARDIRQS
+        select MFD_CORE
+        help
+          Say yes here to support for Maxim Semiconductor MAX77686.
+          This is a Power Management IC with RTC on chip.
+          This driver provides common support for accessing the device;
+          additional drivers must be enabled in order to use the functionality
+          of the device.
+
 config MFD_S5M_CORE
 	bool "SAMSUNG S5M Series Support"
 	depends on I2C=y && GENERIC_HARDIRQS
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 05fa538..3338eb4 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -79,6 +79,7 @@
 obj-$(CONFIG_MFD_MAX8925)	+= max8925.o
 obj-$(CONFIG_MFD_MAX8997)	+= max8997.o max8997-irq.o
 obj-$(CONFIG_MFD_MAX8998)	+= max8998.o max8998-irq.o
+obj-$(CONFIG_MFD_MAX77686)      += max77686.o max77686-irq.o
 
 pcf50633-objs			:= pcf50633-core.o pcf50633-irq.o
 obj-$(CONFIG_MFD_PCF50633)	+= pcf50633.o
diff --git a/drivers/mfd/max77686-irq.c b/drivers/mfd/max77686-irq.c
new file mode 100644
index 0000000..2be1ca1
--- /dev/null
+++ b/drivers/mfd/max77686-irq.c
@@ -0,0 +1,319 @@
+/*
+ * max77686-irq.c - Interrupt controller support for MAX77686
+ *
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Chiwoong Byun <woong.byun@samsung.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
+ *
+ * This driver is based on max8997-irq.c
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+
+enum {
+	MAX77686_DEBUG_IRQ_INFO = 1 << 0,
+	MAX77686_DEBUG_IRQ_MASK = 1 << 1,
+	MAX77686_DEBUG_IRQ_INT = 1 << 2,
+};
+
+static int debug_mask;
+
+static const u8 max77686_mask_reg[] = {
+	[PMIC_INT1] = MAX77686_REG_INT1MSK,
+	[PMIC_INT2] = MAX77686_REG_INT2MSK,
+	[RTC_INT] = MAX77686_RTC_INTM,
+};
+
+static struct i2c_client *max77686_get_i2c(struct max77686_dev *max77686,
+					   enum max77686_irq_source src)
+{
+	switch (src) {
+	case PMIC_INT1...PMIC_INT2:
+		return max77686->i2c;
+	case RTC_INT:
+		return max77686->rtc;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+}
+
+struct max77686_irq_data {
+	int mask;
+	enum max77686_irq_source group;
+};
+
+#define DECLARE_IRQ(_group, _mask)		\
+	{ .group = (_group), .mask = (_mask) }
+static const struct max77686_irq_data max77686_irqs[] = {
+	[MAX77686_PMICIRQ_PWRONF]	= DECLARE_IRQ(PMIC_INT1, 1 << 0),
+	[MAX77686_PMICIRQ_PWRONR]	= DECLARE_IRQ(PMIC_INT1, 1 << 1),
+	[MAX77686_PMICIRQ_JIGONBF]	= DECLARE_IRQ(PMIC_INT1, 1 << 2),
+	[MAX77686_PMICIRQ_JIGONBR]	= DECLARE_IRQ(PMIC_INT1, 1 << 3),
+	[MAX77686_PMICIRQ_ACOKBF]	= DECLARE_IRQ(PMIC_INT1, 1 << 4),
+	[MAX77686_PMICIRQ_ACOKBR]	= DECLARE_IRQ(PMIC_INT1, 1 << 5),
+	[MAX77686_PMICIRQ_ONKEY1S]	= DECLARE_IRQ(PMIC_INT1, 1 << 6),
+	[MAX77686_PMICIRQ_MRSTB]	= DECLARE_IRQ(PMIC_INT1, 1 << 7),
+	[MAX77686_PMICIRQ_140C]		= DECLARE_IRQ(PMIC_INT2, 1 << 0),
+	[MAX77686_PMICIRQ_120C]		= DECLARE_IRQ(PMIC_INT2, 1 << 1),
+	[MAX77686_RTCIRQ_RTC60S]	= DECLARE_IRQ(RTC_INT, 1 << 0),
+	[MAX77686_RTCIRQ_RTCA1]		= DECLARE_IRQ(RTC_INT, 1 << 1),
+	[MAX77686_RTCIRQ_RTCA2]		= DECLARE_IRQ(RTC_INT, 1 << 2),
+	[MAX77686_RTCIRQ_SMPL]		= DECLARE_IRQ(RTC_INT, 1 << 3),
+	[MAX77686_RTCIRQ_RTC1S]		= DECLARE_IRQ(RTC_INT, 1 << 4),
+	[MAX77686_RTCIRQ_WTSR]		= DECLARE_IRQ(RTC_INT, 1 << 5),
+};
+
+static void max77686_irq_lock(struct irq_data *data)
+{
+	struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+
+	if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
+		pr_info("%s\n", __func__);
+
+	mutex_lock(&max77686->irqlock);
+}
+
+static void max77686_irq_sync_unlock(struct irq_data *data)
+{
+	struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+	int i;
+
+	for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
+		u8 mask_reg = max77686_mask_reg[i];
+		struct i2c_client *i2c = max77686_get_i2c(max77686, i);
+
+		if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
+			pr_info("%s: mask_reg[%d]=0x%x, cur=0x%x\n",
+				__func__, i, mask_reg,
+				max77686->irq_masks_cur[i]);
+
+		if (mask_reg == MAX77686_REG_INVALID || IS_ERR_OR_NULL(i2c))
+			continue;
+
+		max77686->irq_masks_cache[i] = max77686->irq_masks_cur[i];
+
+		max77686_write_reg(i2c, max77686_mask_reg[i],
+				   max77686->irq_masks_cur[i]);
+	}
+
+	mutex_unlock(&max77686->irqlock);
+}
+
+static inline const struct max77686_irq_data *irq_to_max77686_irq(
+					struct max77686_dev *max77686, int irq)
+{
+	return &max77686_irqs[irq - max77686->irq_base];
+}
+
+static void max77686_irq_mask(struct irq_data *data)
+{
+	struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+	const struct max77686_irq_data *irq_data =
+	    irq_to_max77686_irq(max77686, data->irq);
+
+	max77686->irq_masks_cur[irq_data->group] |= irq_data->mask;
+
+	if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
+		pr_info("%s: group=%d, cur=0x%x\n",
+			__func__, irq_data->group,
+			max77686->irq_masks_cur[irq_data->group]);
+}
+
+static void max77686_irq_unmask(struct irq_data *data)
+{
+	struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+	const struct max77686_irq_data *irq_data =
+	    irq_to_max77686_irq(max77686, data->irq);
+
+	max77686->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
+
+	if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
+		pr_info("%s: group=%d, cur=0x%x\n",
+			__func__, irq_data->group,
+			max77686->irq_masks_cur[irq_data->group]);
+}
+
+static struct irq_chip max77686_irq_chip = {
+	.name = "max77686",
+	.irq_bus_lock = max77686_irq_lock,
+	.irq_bus_sync_unlock = max77686_irq_sync_unlock,
+	.irq_mask = max77686_irq_mask,
+	.irq_unmask = max77686_irq_unmask,
+};
+
+static irqreturn_t max77686_irq_thread(int irq, void *data)
+{
+	struct max77686_dev *max77686 = data;
+	u8 irq_reg[MAX77686_IRQ_GROUP_NR] = { };
+	u8 irq_src;
+	int ret;
+	int i;
+
+	ret = max77686_read_reg(max77686->i2c, MAX77686_REG_INTSRC, &irq_src);
+	if (ret < 0) {
+		dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
+			ret);
+		return IRQ_NONE;
+	}
+
+	if (debug_mask & MAX77686_DEBUG_IRQ_INT)
+		pr_info("%s: irq_src=0x%x\n", __func__, irq_src);
+
+	/* MAX77686_IRQSRC_RTC may be set even if there are pending at INT1/2 */
+	ret = max77686_read_reg(max77686->i2c, MAX77686_REG_INT1, &irq_reg[0]);
+	ret = max77686_read_reg(max77686->i2c, MAX77686_REG_INT2, &irq_reg[1]);
+	if (ret < 0) {
+		dev_err(max77686->dev, "Failed to read pmic interrupt: %d\n",
+			ret);
+		return IRQ_NONE;
+	}
+
+	if (debug_mask & MAX77686_DEBUG_IRQ_INT)
+		pr_info("%s: int1=0x%x, int2=0x%x\n",
+			__func__, irq_reg[PMIC_INT1], irq_reg[PMIC_INT2]);
+
+	if (irq_src & MAX77686_IRQSRC_RTC) {
+#ifdef CONFIG_RTC_DRV_MAX77686
+		ret =
+		    max77686_read_reg(max77686->rtc, MAX77686_RTC_INT,
+				      &irq_reg[RTC_INT]);
+#else
+		ret = -ENODEV;
+#endif
+		if (ret < 0) {
+			dev_err(max77686->dev,
+				"Failed to read rtc interrupt: %d\n", ret);
+			return IRQ_NONE;
+		}
+
+		if (debug_mask & MAX77686_DEBUG_IRQ_INT)
+			pr_info("%s: rtc int=0x%x\n", __func__,
+				irq_reg[RTC_INT]);
+
+	}
+
+	for (i = 0; i < MAX77686_IRQ_NR; i++) {
+		if (irq_reg[max77686_irqs[i].group] & max77686_irqs[i].mask)
+			handle_nested_irq(max77686->irq_base + i);
+	}
+
+	return IRQ_HANDLED;
+}
+
+int max77686_irq_resume(struct max77686_dev *max77686)
+{
+	if (max77686->irq && max77686->irq_base)
+		max77686_irq_thread(max77686->irq_base, max77686);
+	return 0;
+}
+
+int max77686_irq_init(struct max77686_dev *max77686)
+{
+	int i;
+	int cur_irq;
+	int ret;
+	int val;
+
+	if (debug_mask & MAX77686_DEBUG_IRQ_INFO)
+		pr_info("%s+\n", __func__);
+
+	if (!max77686->irq_gpio) {
+		dev_warn(max77686->dev, "No interrupt gpio specified.\n");
+		max77686->irq_base = 0;
+		return 0;
+	}
+
+	if (!max77686->irq_base) {
+		dev_err(max77686->dev, "No interrupt base specified.\n");
+		return 0;
+	}
+
+	mutex_init(&max77686->irqlock);
+
+	max77686->irq = gpio_to_irq(max77686->irq_gpio);
+	ret = gpio_request(max77686->irq_gpio, "pmic_irq");
+	if (ret < 0 && ret != -EBUSY) {
+		dev_err(max77686->dev,
+			"Failed to request gpio %d with ret: %d\n",
+			max77686->irq_gpio, ret);
+		return IRQ_NONE;
+	}
+	if (ret == -EBUSY)
+		dev_warn(max77686->dev, "gpio pmic_irq is already requested\n");
+
+	gpio_direction_input(max77686->irq_gpio);
+	val = gpio_get_value(max77686->irq_gpio);
+	gpio_free(max77686->irq_gpio);
+
+	if (debug_mask & MAX77686_DEBUG_IRQ_INT)
+		pr_info("%s: gpio_irq=%x\n", __func__, val);
+
+	/* Mask individual interrupt sources */
+	for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
+		struct i2c_client *i2c;
+
+		max77686->irq_masks_cur[i] = 0xff;
+		max77686->irq_masks_cache[i] = 0xff;
+		i2c = max77686_get_i2c(max77686, i);
+
+		if (IS_ERR_OR_NULL(i2c))
+			continue;
+		if (max77686_mask_reg[i] == MAX77686_REG_INVALID)
+			continue;
+
+		max77686_write_reg(i2c, max77686_mask_reg[i], 0xff);
+	}
+
+	/* Register with genirq */
+	for (i = 0; i < MAX77686_IRQ_NR; i++) {
+		cur_irq = i + max77686->irq_base;
+		irq_set_chip_data(cur_irq, max77686);
+		irq_set_chip_and_handler(cur_irq, &max77686_irq_chip,
+					 handle_edge_irq);
+		irq_set_nested_thread(cur_irq, 1);
+#ifdef CONFIG_ARM
+		set_irq_flags(cur_irq, IRQF_VALID);
+#else
+		irq_set_noprobe(cur_irq);
+#endif
+	}
+
+	ret = request_threaded_irq(max77686->irq, NULL, max77686_irq_thread,
+				   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+				   "max77686-irq", max77686);
+
+	if (ret) {
+		dev_err(max77686->dev, "Failed to request IRQ %d: %d\n",
+			max77686->irq, ret);
+		return ret;
+	}
+
+	if (debug_mask & MAX77686_DEBUG_IRQ_INFO)
+		pr_info("%s-\n", __func__);
+
+	return 0;
+}
+
+void max77686_irq_exit(struct max77686_dev *max77686)
+{
+	if (max77686->irq)
+		free_irq(max77686->irq, max77686);
+}
diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c
new file mode 100644
index 0000000..7359f21
--- /dev/null
+++ b/drivers/mfd/max77686.c
@@ -0,0 +1,255 @@
+/*
+ * max77686.c - mfd core driver for the Maxim 77686
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Chiwoong Byun <woong.byun@smasung.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
+ *
+ * This driver is based on max8997.c
+ */
+
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#define I2C_ADDR_RTC	(0x0C >> 1)
+
+static struct mfd_cell max77686_devs[] = {
+	{ .name = "max77686-pmic", },
+	{ .name = "max77686-rtc", },
+};
+
+int max77686_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
+{
+	struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&max77686->iolock);
+	ret = i2c_smbus_read_byte_data(i2c, reg);
+	mutex_unlock(&max77686->iolock);
+	if (ret < 0)
+		return ret;
+
+	ret &= 0xff;
+	*dest = ret;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max77686_read_reg);
+
+int max77686_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
+{
+	struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&max77686->iolock);
+	ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf);
+	mutex_unlock(&max77686->iolock);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max77686_bulk_read);
+
+int max77686_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
+{
+	struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&max77686->iolock);
+	ret = i2c_smbus_write_byte_data(i2c, reg, value);
+	mutex_unlock(&max77686->iolock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(max77686_write_reg);
+
+int max77686_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
+{
+	struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&max77686->iolock);
+	ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf);
+	mutex_unlock(&max77686->iolock);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max77686_bulk_write);
+
+int max77686_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
+{
+	struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&max77686->iolock);
+	ret = i2c_smbus_read_byte_data(i2c, reg);
+	if (ret >= 0) {
+		u8 old_val = ret & 0xff;
+		u8 new_val = (val & mask) | (old_val & (~mask));
+		ret = i2c_smbus_write_byte_data(i2c, reg, new_val);
+	}
+	mutex_unlock(&max77686->iolock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(max77686_update_reg);
+
+static int max77686_i2c_probe(struct i2c_client *i2c,
+			      const struct i2c_device_id *id)
+{
+	struct max77686_dev *max77686;
+	struct max77686_platform_data *pdata = i2c->dev.platform_data;
+	u8 data;
+	int ret = 0;
+
+	max77686 = kzalloc(sizeof(struct max77686_dev), GFP_KERNEL);
+	if (max77686 == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, max77686);
+	max77686->dev = &i2c->dev;
+	max77686->i2c = i2c;
+	max77686->type = id->driver_data;
+
+	if (!pdata) {
+		ret = -EIO;
+		goto err;
+	}
+
+	max77686->wakeup = pdata->wakeup;
+	max77686->irq_gpio = pdata->irq_gpio;
+	max77686->irq_base = pdata->irq_base;
+
+	mutex_init(&max77686->iolock);
+
+	if (max77686_read_reg(i2c, MAX77686_REG_DEVICE_ID, &data) < 0) {
+		dev_err(max77686->dev,
+			"device not found on this channel (this is not an error)\n");
+		ret = -ENODEV;
+		goto err;
+	} else
+		dev_info(max77686->dev, "device found\n");
+
+	max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
+	i2c_set_clientdata(max77686->rtc, max77686);
+
+	max77686_irq_init(max77686);
+
+	ret = mfd_add_devices(max77686->dev, -1, max77686_devs,
+			      ARRAY_SIZE(max77686_devs), NULL, 0);
+
+	if (ret < 0)
+		goto err_mfd;
+
+	device_init_wakeup(max77686->dev, pdata->wakeup);
+
+	return ret;
+
+err_mfd:
+	mfd_remove_devices(max77686->dev);
+	i2c_unregister_device(max77686->rtc);
+err:
+	kfree(max77686);
+	return ret;
+}
+
+static int max77686_i2c_remove(struct i2c_client *i2c)
+{
+	struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+	mfd_remove_devices(max77686->dev);
+	i2c_unregister_device(max77686->rtc);
+	kfree(max77686);
+
+	return 0;
+}
+
+static const struct i2c_device_id max77686_i2c_id[] = {
+	{ "max77686", TYPE_MAX77686 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, max77686_i2c_id);
+
+#ifdef CONFIG_PM
+static int max77686_suspend(struct device *dev)
+{
+	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+	struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+	disable_irq(max77686->irq);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(max77686->irq);
+
+	return 0;
+}
+
+static int max77686_resume(struct device *dev)
+{
+	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+	struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(max77686->irq);
+
+	enable_irq(max77686->irq);
+
+	return max77686_irq_resume(max77686);
+}
+#else
+#define max77686_suspend	NULL
+#define max77686_resume		NULL
+#endif /* CONFIG_PM */
+
+const struct dev_pm_ops max77686_pm = {
+	.suspend = max77686_suspend,
+	.resume = max77686_resume,
+};
+
+static struct i2c_driver max77686_i2c_driver = {
+	.driver = {
+		.name = "max77686",
+		.owner = THIS_MODULE,
+		.pm = &max77686_pm,
+	},
+	.probe = max77686_i2c_probe,
+	.remove = max77686_i2c_remove,
+	.id_table = max77686_i2c_id,
+};
+
+static int __init max77686_i2c_init(void)
+{
+	return i2c_add_driver(&max77686_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(max77686_i2c_init);
+
+static void __exit max77686_i2c_exit(void)
+{
+	i2c_del_driver(&max77686_i2c_driver);
+}
+module_exit(max77686_i2c_exit);
+
+MODULE_DESCRIPTION("MAXIM 77686 multi-function core driver");
+MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index 8698a8e..0d36891 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -196,6 +196,7 @@
 {
 	struct wm8994 *wm8994 = dev_get_drvdata(dev);
 	int ret;
+	int gpio_regs[WM8994_NUM_GPIO_REGS];
 
 	/* Don't actually go through with the suspend if the CODEC is
 	 * still active (eg, for audio passthrough from CP. */
@@ -277,12 +278,24 @@
 				WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD,
 				WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD);
 
+	/* Save GPIO registers before reset */
+	regmap_bulk_read(wm8994->regmap, WM8994_GPIO_1, gpio_regs,
+			 WM8994_NUM_GPIO_REGS);
+
 	/* Explicitly put the device into reset in case regulators
 	 * don't get disabled in order to ensure consistent restart.
 	 */
 	wm8994_reg_write(wm8994, WM8994_SOFTWARE_RESET,
 			 wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET));
 
+	/* Restore GPIO registers to prevent problems with mismatched
+	 * pin configurations.
+	 */
+	ret = regmap_bulk_write(wm8994->regmap, WM8994_GPIO_1, gpio_regs,
+				WM8994_NUM_GPIO_REGS);
+	if (ret != 0)
+		dev_err(dev, "Failed to restore GPIO registers: %d\n", ret);
+
 	regcache_cache_only(wm8994->regmap, true);
 	regcache_mark_dirty(wm8994->regmap);
 
@@ -500,7 +513,8 @@
 			ret);
 		goto err_enable;
 	}
-	wm8994->revision = ret;
+	wm8994->revision = ret & WM8994_CHIP_REV_MASK;
+	wm8994->cust_id = (ret & WM8994_CUST_ID_MASK) >> WM8994_CUST_ID_SHIFT;
 
 	switch (wm8994->type) {
 	case WM8994:
@@ -554,8 +568,8 @@
 		break;
 	}
 
-	dev_info(wm8994->dev, "%s revision %c\n", devname,
-		 'A' + wm8994->revision);
+	dev_info(wm8994->dev, "%s revision %c CUST_ID %02x\n", devname,
+		 'A' + wm8994->revision, wm8994->cust_id);
 
 	switch (wm8994->type) {
 	case WM1811:
@@ -733,23 +747,7 @@
 	.id_table = wm8994_i2c_id,
 };
 
-static int __init wm8994_i2c_init(void)
-{
-	int ret;
-
-	ret = i2c_add_driver(&wm8994_i2c_driver);
-	if (ret != 0)
-		pr_err("Failed to register wm8994 I2C driver: %d\n", ret);
-
-	return ret;
-}
-module_init(wm8994_i2c_init);
-
-static void __exit wm8994_i2c_exit(void)
-{
-	i2c_del_driver(&wm8994_i2c_driver);
-}
-module_exit(wm8994_i2c_exit);
+module_i2c_driver(wm8994_i2c_driver);
 
 MODULE_DESCRIPTION("Core support for the WM8994 audio CODEC");
 MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c
index 46b20c4..2dc368d 100644
--- a/drivers/mfd/wm8994-irq.c
+++ b/drivers/mfd/wm8994-irq.c
@@ -21,6 +21,7 @@
 #include <linux/regmap.h>
 
 #include <linux/mfd/wm8994/core.h>
+#include <linux/mfd/wm8994/pdata.h>
 #include <linux/mfd/wm8994/registers.h>
 
 #include <linux/delay.h>
@@ -139,6 +140,8 @@
 int wm8994_irq_init(struct wm8994 *wm8994)
 {
 	int ret;
+	unsigned long irqflags;
+	struct wm8994_pdata *pdata = wm8994->dev->platform_data;
 
 	if (!wm8994->irq) {
 		dev_warn(wm8994->dev,
@@ -153,8 +156,13 @@
 		return 0;
 	}
 
+	/* select user or default irq flags */
+	irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
+	if (pdata->irq_flags)
+		irqflags = pdata->irq_flags;
+
 	ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq,
-				  IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+				  irqflags,
 				  wm8994->irq_base, &wm8994_irq_chip,
 				  &wm8994->irq_data);
 	if (ret != 0) {
diff --git a/drivers/mfd/wm8994-regmap.c b/drivers/mfd/wm8994-regmap.c
index bfd25af..2fbce9c 100644
--- a/drivers/mfd/wm8994-regmap.c
+++ b/drivers/mfd/wm8994-regmap.c
@@ -1122,7 +1122,6 @@
 	case WM8994_RATE_STATUS:
 	case WM8958_MIC_DETECT_3:
 	case WM8994_DC_SERVO_4E:
-	case WM8994_CHIP_REVISION:
 	case WM8994_INTERRUPT_STATUS_1:
 	case WM8994_INTERRUPT_STATUS_2:
 		return true;
@@ -1137,7 +1136,7 @@
 
 	switch (reg) {
 	case WM8994_GPIO_6:
-		if (wm8994->revision > 1)
+		if (wm8994->cust_id > 1 || wm8994->revision > 1)
 			return true;
 		else
 			return false;
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 1a0254a..8607422 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -267,6 +267,17 @@
 	  MFGPTs have a better resolution and max interval than the
 	  generic PIT, and are suitable for use as high-res timers.
 
+config HAPTIC_ISA1200
+	tristate "Imagis ISA1200 Haptic Controller"
+	depends on I2C && ANDROID_TIMED_OUTPUT && HAVE_PWM
+	default n
+	help
+	  If you say yes here you get support for the Imagis ISA1200 haptic
+	  controller. ISA1200 is a haptic motor driver with register based
+	  architecture, allowing for I2C control.
+	  This driver provides haptic motor control and is accessed as a
+	  timed output device.
+
 config HP_ILO
 	tristate "Channel interface driver for the HP iLO processor"
 	depends on PCI
@@ -304,6 +315,16 @@
 	This option enables addition debugging code for the SGI GRU driver. If
 	you are unsure, say N.
 
+config SAMSUNG_JACK
+	bool "3.5MM ear jack driver for Samsung devices"
+	depends on INPUT
+	depends on SWITCH
+	default n
+	---help---
+	This is 3.5MM ear jack driver for Samsung devices.
+
+	If unsure, say N.
+
 config APDS9802ALS
 	tristate "Medfield Avago APDS9802 ALS Sensor module"
 	depends on I2C
@@ -517,7 +538,22 @@
 	---help---
 	 Creates an rfkill entry in sysfs for power control of Bluetooth
 	 TI wl127x chips.
-	 
+
+config STMPE811_ADC
+	tristate "STMPE811 ADC driver"
+	depends on I2C
+	help
+	 If you say yes here you get support for the STMicroelectronics
+	 STMPE811 8 channel ADC driver.
+
+config AUDIENCE_ES305
+	tristate "Audience ES305 Voice Processor"
+	depends on I2C
+	default n
+	help
+	  The ES305 is a Voice Processor with integrated DSP that processes
+	  i2s audio data to provide noise suppression and echo cancellation.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 89540d1..f42af7a 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -25,8 +25,10 @@
 obj-$(CONFIG_SGI_XP)		+= sgi-xp/
 obj-$(CONFIG_SGI_GRU)		+= sgi-gru/
 obj-$(CONFIG_CS5535_MFGPT)	+= cs5535-mfgpt.o
+obj-$(CONFIG_HAPTIC_ISA1200)	+= haptic_isa1200.o
 obj-$(CONFIG_HP_ILO)		+= hpilo.o
 obj-$(CONFIG_APDS9802ALS)	+= apds9802als.o
+obj-$(CONFIG_SAMSUNG_JACK)	+= sec_jack.o
 obj-$(CONFIG_ISL29003)		+= isl29003.o
 obj-$(CONFIG_ISL29020)		+= isl29020.o
 obj-$(CONFIG_SENSORS_TSL2550)	+= tsl2550.o
@@ -52,3 +54,5 @@
 obj-$(CONFIG_MAX8997_MUIC)	+= max8997-muic.o
 obj-$(CONFIG_WL127X_RFKILL)	+= wl127x-rfkill.o
 obj-$(CONFIG_SENSORS_AK8975)	+= akm8975.o
+obj-$(CONFIG_STMPE811_ADC)	+= stmpe811-adc.o
+obj-$(CONFIG_AUDIENCE_ES305)	+= es305.o
diff --git a/drivers/misc/es305.c b/drivers/misc/es305.c
new file mode 100644
index 0000000..98887a5
--- /dev/null
+++ b/drivers/misc/es305.c
@@ -0,0 +1,808 @@
+/*
+ * drivers/misc/es305.c - Audience ES305 Voice Processor driver
+ *
+ * Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2012 Samsung Corporation.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/platform_data/es305.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#define ES305_FIRMWARE_NAME		"es305_fw.bin"
+#define MAX_CMD_SEND_SIZE		32
+#define RETRY_COUNT			5
+
+/* ES305 commands and values */
+#define ES305_BOOT			0x0001
+#define ES305_BOOT_ACK			0x01
+
+#define ES305_SYNC_POLLING		0x80000000
+
+#define ES305_RESET_IMMEDIATE		0x80020000
+
+#define ES305_SET_POWER_STATE_SLEEP	0x80100001
+
+#define ES305_GET_ALGORITHM_PARM	0x80160000
+#define ES305_SET_ALGORITHM_PARM_ID	0x80170000
+#define ES305_SET_ALGORITHM_PARM	0x80180000
+#define ES305_AEC_MODE			0x0003
+#define ES305_TX_AGC			0x0004
+#define ES305_RX_AGC			0x0028
+#define ES305_TX_NS_LEVEL		0x004B
+#define ES305_RX_NS_LEVEL		0x004C
+#define ES305_ALGORITHM_RESET		0x001C
+
+#define ES305_GET_VOICE_PROCESSING	0x80430000
+#define ES305_SET_VOICE_PROCESSING	0x801C0000
+
+#define ES305_GET_AUDIO_ROUTING		0x80270000
+#define ES305_SET_AUDIO_ROUTING		0x80260000
+
+#define ES305_SET_PRESET		0x80310000
+
+#define ES305_GET_MIC_SAMPLE_RATE	0x80500000
+#define ES305_SET_MIC_SAMPLE_RATE	0x80510000
+#define ES305_8KHZ			0x0008
+#define ES305_16KHZ			0x000A
+#define ES305_48KHZ			0x0030
+
+#define ES305_DIGITAL_PASSTHROUGH	0x80520000
+
+struct es305_data {
+	struct es305_platform_data	*pdata;
+	struct device			*dev;
+	struct i2c_client		*client;
+	const struct firmware		*fw;
+	struct mutex			lock;
+	u32				passthrough;
+	bool				passthrough_on;
+	bool				asleep;
+	bool				device_ready;
+};
+
+static int es305_send_cmd(struct es305_data *es305, u32 command, u16 *response)
+{
+	u8 send[4];
+	u8 recv[4];
+	int ret = 0;
+	int retry = RETRY_COUNT;
+
+	send[0] = (command >> 24) & 0xff;
+	send[1] = (command >> 16) & 0xff;
+	send[2] = (command >> 8) & 0xff;
+	send[3] = command & 0xff;
+
+	ret = i2c_master_send(es305->client, send, 4);
+	if (ret < 0) {
+		dev_err(es305->dev, "i2c_master_send failed\n");
+		return ret;
+	}
+
+	/* The sleep command cannot be acked before the device goes to sleep */
+	if (command == ES305_SET_POWER_STATE_SLEEP)
+		return ret;
+
+	usleep_range(1000, 2000);
+	while (retry--) {
+		ret = i2c_master_recv(es305->client, recv, 4);
+		if (ret < 0) {
+			dev_err(es305->dev, "i2c_master_recv failed\n");
+			return ret;
+		}
+		/*
+		 * Check that the first two bytes of the response match
+		 * (the ack is in those bytes)
+		 */
+		if ((send[0] == recv[0]) && (send[1] == recv[1])) {
+			if (response)
+				*response = (recv[2] << 8) | recv[3];
+			ret = 0;
+			break;
+		} else {
+			dev_err(es305->dev,
+				"incorrect ack (got 0x%.2x%.2x)\n",
+				recv[0], recv[1]);
+			ret = -EINVAL;
+		}
+
+		/* Wait before polling again */
+		if (retry > 0)
+			msleep(20);
+	}
+
+	return ret;
+}
+
+static int es305_load_firmware(struct es305_data *es305)
+{
+	int ret = 0;
+	const u8 *i2c_cmds;
+	int size;
+
+	i2c_cmds = es305->fw->data;
+	size = es305->fw->size;
+
+	while (size > 0) {
+		ret = i2c_master_send(es305->client, i2c_cmds,
+				min(size, MAX_CMD_SEND_SIZE));
+		if (ret < 0) {
+			dev_err(es305->dev, "i2c_master_send failed\n");
+			break;
+		}
+		size -= MAX_CMD_SEND_SIZE;
+		i2c_cmds += MAX_CMD_SEND_SIZE;
+	}
+
+	return ret;
+}
+
+static int es305_reset(struct es305_data *es305)
+{
+	int ret = 0;
+	struct es305_platform_data *pdata = es305->pdata;
+	static const u8 boot[2] = {ES305_BOOT >> 8, ES305_BOOT};
+	u8 ack;
+	int retry = RETRY_COUNT;
+
+	while (retry--) {
+		/* Reset ES305 chip */
+		gpio_set_value(pdata->gpio_reset, 0);
+		usleep_range(200, 400);
+		gpio_set_value(pdata->gpio_reset, 1);
+
+		/* Delay before sending i2c commands */
+		msleep(50);
+
+		/*
+		 * Send boot command and check response. The boot command
+		 * is different from the others in that it's only 2 bytes,
+		 * and the ack retry mechanism is different too.
+		 */
+		ret = i2c_master_send(es305->client, boot, 2);
+		if (ret < 0) {
+			dev_err(es305->dev, "i2c_master_send failed\n");
+			continue;
+		}
+		usleep_range(1000, 2000);
+		ret = i2c_master_recv(es305->client, &ack, 1);
+		if (ret < 0) {
+			dev_err(es305->dev, "i2c_master_recv failed\n");
+			continue;
+		}
+		if (ack != ES305_BOOT_ACK) {
+			dev_err(es305->dev, "boot ack incorrect (got 0x%.2x)\n",
+									ack);
+			continue;
+		}
+
+		ret = es305_load_firmware(es305);
+		if (ret < 0) {
+			dev_err(es305->dev, "load firmware error\n");
+			continue;
+		}
+
+		/* Delay before issuing a sync command */
+		msleep(120);
+
+		ret = es305_send_cmd(es305, ES305_SYNC_POLLING, NULL);
+		if (ret < 0) {
+			dev_err(es305->dev, "sync error\n");
+			continue;
+		}
+
+		break;
+	}
+
+	return ret;
+}
+
+static int es305_sleep(struct es305_data *es305)
+{
+	int ret = 0;
+	struct es305_platform_data *pdata = es305->pdata;
+
+	if (es305->asleep)
+		return ret;
+
+	ret = es305_send_cmd(es305, ES305_SET_POWER_STATE_SLEEP, NULL);
+	if (ret < 0) {
+		dev_err(es305->dev, "set power state error\n");
+		return ret;
+	}
+
+	/* The clock can be disabled after the device has had time to sleep */
+	msleep(20);
+	pdata->clk_enable(false);
+	gpio_set_value(pdata->gpio_wakeup, 1);
+
+	es305->asleep = true;
+
+	return ret;
+}
+
+static int es305_wake(struct es305_data *es305)
+{
+	int ret = 0;
+	struct es305_platform_data *pdata = es305->pdata;
+
+	if (!es305->asleep)
+		return ret;
+
+	pdata->clk_enable(true);
+	gpio_set_value(pdata->gpio_wakeup, 0);
+	msleep(30);
+
+	ret = es305_send_cmd(es305, ES305_SYNC_POLLING, NULL);
+	if (ret < 0) {
+		dev_err(es305->dev, "sync error\n");
+
+		/* Go back to sleep */
+		pdata->clk_enable(false);
+		gpio_set_value(pdata->gpio_wakeup, 1);
+		return ret;
+	}
+
+	es305->asleep = false;
+
+	return ret;
+}
+
+static int es305_set_passthrough(struct es305_data *es305, u32 path)
+{
+	int ret;
+
+	ret = es305_send_cmd(es305, ES305_DIGITAL_PASSTHROUGH | path, NULL);
+	if (ret < 0) {
+		dev_err(es305->dev, "set passthrough error\n");
+		return ret;
+	}
+
+	es305->passthrough_on = !!path;
+
+	return ret;
+}
+
+static ssize_t es305_audio_routing_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct es305_data *es305 = dev_get_drvdata(dev);
+	int ret;
+	u16 val;
+
+	if (!es305->device_ready)
+		return -EAGAIN;
+
+	mutex_lock(&es305->lock);
+	ret = es305_wake(es305);
+	if (ret < 0) {
+		dev_err(es305->dev, "unable to wake\n");
+		mutex_unlock(&es305->lock);
+		return ret;
+	}
+
+	ret = es305_send_cmd(es305, ES305_GET_AUDIO_ROUTING, &val);
+	if (ret < 0) {
+		dev_err(es305->dev, "get audio routing error\n");
+		mutex_unlock(&es305->lock);
+		return ret;
+	}
+
+	mutex_unlock(&es305->lock);
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t es305_audio_routing_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct es305_data *es305 = dev_get_drvdata(dev);
+	unsigned long val;
+	int ret;
+
+	if (!es305->device_ready)
+		return -EAGAIN;
+
+	ret = kstrtoul(buf, 0, &val);
+	if (ret)
+		return -EINVAL;
+
+	mutex_lock(&es305->lock);
+	ret = es305_wake(es305);
+	if (ret < 0) {
+		dev_err(es305->dev, "unable to wake\n");
+		mutex_unlock(&es305->lock);
+		return ret;
+	}
+
+	ret = es305_send_cmd(es305, ES305_SET_AUDIO_ROUTING | (u32)val, NULL);
+	if (ret < 0) {
+		dev_err(es305->dev, "set audio routing error\n");
+		mutex_unlock(&es305->lock);
+		return ret;
+	}
+
+	mutex_unlock(&es305->lock);
+	return size;
+}
+
+static ssize_t es305_preset_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct es305_data *es305 = dev_get_drvdata(dev);
+	unsigned long val;
+	int ret;
+
+	if (!es305->device_ready)
+		return -EAGAIN;
+
+	ret = kstrtoul(buf, 0, &val);
+	if (ret)
+		return -EINVAL;
+
+	mutex_lock(&es305->lock);
+	ret = es305_wake(es305);
+	if (ret < 0) {
+		dev_err(es305->dev, "unable to wake\n");
+		mutex_unlock(&es305->lock);
+		return ret;
+	}
+
+	ret = es305_send_cmd(es305, ES305_SET_PRESET | (u32)val, NULL);
+	if (ret < 0) {
+		dev_err(es305->dev, "set preset error\n");
+		mutex_unlock(&es305->lock);
+		return ret;
+	}
+
+	mutex_unlock(&es305->lock);
+	return size;
+}
+
+static ssize_t es305_voice_processing_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct es305_data *es305 = dev_get_drvdata(dev);
+	int ret;
+	u16 val;
+
+	if (!es305->device_ready)
+		return -EAGAIN;
+
+	mutex_lock(&es305->lock);
+	ret = es305_wake(es305);
+	if (ret < 0) {
+		dev_err(es305->dev, "unable to wake\n");
+		mutex_unlock(&es305->lock);
+		return ret;
+	}
+
+	ret = es305_send_cmd(es305, ES305_GET_VOICE_PROCESSING, &val);
+	if (ret < 0) {
+		dev_err(es305->dev, "get voice processing error\n");
+		mutex_unlock(&es305->lock);
+		return ret;
+	}
+
+	mutex_unlock(&es305->lock);
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t es305_voice_processing_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct es305_data *es305 = dev_get_drvdata(dev);
+	unsigned long val;
+	int ret;
+
+	if (!es305->device_ready)
+		return -EAGAIN;
+
+	ret = kstrtoul(buf, 0, &val);
+	if (ret)
+		return -EINVAL;
+
+	mutex_lock(&es305->lock);
+	ret = es305_wake(es305);
+	if (ret < 0) {
+		dev_err(es305->dev, "unable to wake\n");
+		mutex_unlock(&es305->lock);
+		return ret;
+	}
+
+	/*
+	 * If voice processing is being switched on and passthrough is
+	 * enabled, disable passthrough first.
+	 */
+	if (val && es305->passthrough_on) {
+		ret = es305_set_passthrough(es305, 0);
+		if (ret < 0) {
+			dev_err(es305->dev, "unable to disable passthrough\n");
+			mutex_unlock(&es305->lock);
+			return ret;
+		}
+	}
+
+	ret = es305_send_cmd(es305, ES305_SET_VOICE_PROCESSING | (u32)val,
+									NULL);
+	if (ret < 0) {
+		dev_err(es305->dev, "set voice processing error\n");
+		mutex_unlock(&es305->lock);
+		return ret;
+	}
+
+	mutex_unlock(&es305->lock);
+	return size;
+}
+
+static ssize_t es305_sleep_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct es305_data *es305 = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", es305->asleep ? 1 : 0);
+}
+
+static ssize_t es305_sleep_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct es305_data *es305 = dev_get_drvdata(dev);
+	unsigned long state;
+	int ret;
+
+	if (!es305->device_ready)
+		return -EAGAIN;
+
+	ret = kstrtoul(buf, 0, &state);
+	if (ret)
+		return -EINVAL;
+
+	if (!!state ^ es305->asleep) {
+		/* requested sleep state is different to current state */
+		mutex_lock(&es305->lock);
+		if (state) {
+			if (es305->passthrough != 0) {
+				ret = es305_set_passthrough(es305,
+							es305->passthrough);
+				if (ret < 0)
+					dev_err(es305->dev,
+						"unable to set passthrough\n");
+			}
+			ret = es305_sleep(es305);
+		} else {
+			ret = es305_wake(es305);
+		}
+		if (ret < 0) {
+			dev_err(es305->dev, "unable to change sleep state\n");
+			mutex_unlock(&es305->lock);
+			return ret;
+		}
+		mutex_unlock(&es305->lock);
+	}
+
+	return size;
+}
+
+static ssize_t es305_algorithm_parm_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct es305_data *es305 = dev_get_drvdata(dev);
+	int ret;
+	u16 val;
+	u32 parm;
+
+	if (!es305->device_ready)
+		return -EAGAIN;
+
+	if (strcmp(attr->attr.name, "aec_enable") == 0)
+		parm = ES305_AEC_MODE;
+	else if (strcmp(attr->attr.name, "tx_agc_enable") == 0)
+		parm = ES305_TX_AGC;
+	else if (strcmp(attr->attr.name, "rx_agc_enable") == 0)
+		parm = ES305_RX_AGC;
+	else if (strcmp(attr->attr.name, "tx_ns_level") == 0)
+		parm = ES305_TX_NS_LEVEL;
+	else if (strcmp(attr->attr.name, "rx_ns_level") == 0)
+		parm = ES305_RX_NS_LEVEL;
+	else
+		return -EINVAL;
+
+	mutex_lock(&es305->lock);
+	ret = es305_wake(es305);
+	if (ret < 0) {
+		dev_err(es305->dev, "unable to wake\n");
+		mutex_unlock(&es305->lock);
+		return ret;
+	}
+
+	ret = es305_send_cmd(es305, ES305_GET_ALGORITHM_PARM | parm, &val);
+	if (ret < 0) {
+		dev_err(es305->dev, "get algorithm parm error\n");
+		mutex_unlock(&es305->lock);
+		return ret;
+	}
+
+	mutex_unlock(&es305->lock);
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t es305_algorithm_parm_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct es305_data *es305 = dev_get_drvdata(dev);
+	unsigned long val;
+	unsigned long val_max = 1;
+	int ret;
+	u32 parm;
+
+	if (!es305->device_ready)
+		return -EAGAIN;
+
+	if (strcmp(attr->attr.name, "aec_enable") == 0) {
+		parm = ES305_AEC_MODE;
+	} else if (strcmp(attr->attr.name, "tx_agc_enable") == 0) {
+		parm = ES305_TX_AGC;
+	} else if (strcmp(attr->attr.name, "rx_agc_enable") == 0) {
+		parm = ES305_RX_AGC;
+	} else if (strcmp(attr->attr.name, "tx_ns_level") == 0) {
+		parm = ES305_TX_NS_LEVEL;
+		val_max = 10;
+	} else if (strcmp(attr->attr.name, "rx_ns_level") == 0) {
+		parm = ES305_RX_NS_LEVEL;
+		val_max = 10;
+	} else if (strcmp(attr->attr.name, "algorithm_reset") == 0) {
+		parm = ES305_ALGORITHM_RESET;
+		val_max = 0xffff;
+	} else {
+		return -EINVAL;
+	}
+
+	/* Check that a valid value was obtained */
+	ret = kstrtoul(buf, 0, &val);
+	if (ret || (val > val_max))
+		return -EINVAL;
+
+	mutex_lock(&es305->lock);
+	ret = es305_wake(es305);
+	if (ret < 0) {
+		dev_err(es305->dev, "unable to wake\n");
+		mutex_unlock(&es305->lock);
+		return ret;
+	}
+
+	ret = es305_send_cmd(es305, ES305_SET_ALGORITHM_PARM_ID | parm, NULL);
+	if (ret < 0) {
+		dev_err(es305->dev, "set algorithm parm id error\n");
+		mutex_unlock(&es305->lock);
+		return ret;
+	}
+	ret = es305_send_cmd(es305, ES305_SET_ALGORITHM_PARM | (u32)val, NULL);
+	if (ret < 0) {
+		dev_err(es305->dev, "set algorithm parm error\n");
+		mutex_unlock(&es305->lock);
+		return ret;
+	}
+
+	mutex_unlock(&es305->lock);
+	return size;
+}
+
+static DEVICE_ATTR(audio_routing, S_IRUGO | S_IWUSR,
+		es305_audio_routing_show, es305_audio_routing_store);
+static DEVICE_ATTR(preset, S_IWUSR,
+		NULL, es305_preset_store);
+static DEVICE_ATTR(voice_processing, S_IRUGO | S_IWUSR,
+		es305_voice_processing_show, es305_voice_processing_store);
+static DEVICE_ATTR(sleep, S_IRUGO | S_IWUSR,
+		es305_sleep_show, es305_sleep_store);
+static DEVICE_ATTR(aec_enable, S_IRUGO | S_IWUSR,
+		es305_algorithm_parm_show, es305_algorithm_parm_store);
+static DEVICE_ATTR(tx_agc_enable, S_IRUGO | S_IWUSR,
+		es305_algorithm_parm_show, es305_algorithm_parm_store);
+static DEVICE_ATTR(rx_agc_enable, S_IRUGO | S_IWUSR,
+		es305_algorithm_parm_show, es305_algorithm_parm_store);
+static DEVICE_ATTR(tx_ns_level, S_IRUGO | S_IWUSR,
+		es305_algorithm_parm_show, es305_algorithm_parm_store);
+static DEVICE_ATTR(rx_ns_level, S_IRUGO | S_IWUSR,
+		es305_algorithm_parm_show, es305_algorithm_parm_store);
+static DEVICE_ATTR(algorithm_reset, S_IWUSR,
+		NULL, es305_algorithm_parm_store);
+
+static struct attribute *es305_attributes[] = {
+	&dev_attr_audio_routing.attr,
+	&dev_attr_preset.attr,
+	&dev_attr_voice_processing.attr,
+	&dev_attr_sleep.attr,
+	&dev_attr_aec_enable.attr,
+	&dev_attr_tx_agc_enable.attr,
+	&dev_attr_rx_agc_enable.attr,
+	&dev_attr_tx_ns_level.attr,
+	&dev_attr_rx_ns_level.attr,
+	&dev_attr_algorithm_reset.attr,
+	NULL,
+};
+
+static const struct attribute_group es305_attribute_group = {
+	.attrs = es305_attributes,
+};
+
+/*
+ * This is the callback function passed to request_firmware_nowait(),
+ * and will be called as soon as the firmware is ready.
+ */
+static void es305_firmware_ready(const struct firmware *fw, void *context)
+{
+	struct es305_data *es305 = (struct es305_data *)context;
+	struct es305_platform_data *pdata = es305->pdata;
+	int ret;
+
+	if (!fw) {
+		dev_err(es305->dev, "firmware request failed\n");
+		return;
+	}
+	es305->fw = fw;
+
+	pdata->clk_enable(true);
+
+	ret = es305_reset(es305);
+	if (ret < 0) {
+		dev_err(es305->dev, "unable to reset device\n");
+		goto err;
+	}
+
+	/* Enable passthrough if needed */
+	if (es305->passthrough != 0) {
+		ret = es305_set_passthrough(es305, es305->passthrough);
+		if (ret < 0) {
+			dev_err(es305->dev,
+				"unable to enable digital passthrough\n");
+			goto err;
+		}
+	}
+
+	ret = es305_sleep(es305);
+	if (ret < 0) {
+		dev_err(es305->dev, "unable to sleep\n");
+		goto err;
+	}
+
+	es305->device_ready = true;
+
+err:
+	release_firmware(es305->fw);
+	es305->fw = NULL;
+}
+
+static int __devinit es305_probe(struct i2c_client *client,
+				const struct i2c_device_id *id)
+{
+	struct es305_data *es305;
+	struct es305_platform_data *pdata = client->dev.platform_data;
+	int ret = 0;
+
+	es305 = kzalloc(sizeof(*es305), GFP_KERNEL);
+	if (es305 == NULL) {
+		ret = -ENOMEM;
+		goto err_kzalloc;
+	}
+
+	es305->client = client;
+	i2c_set_clientdata(client, es305);
+
+	es305->dev = &client->dev;
+	dev_set_drvdata(es305->dev, es305);
+
+	es305->pdata = pdata;
+
+	if ((pdata->passthrough_src < 0) || (pdata->passthrough_src > 4) ||
+					(pdata->passthrough_dst < 0) ||
+					(pdata->passthrough_dst > 4) ||
+					!pdata->clk_enable) {
+		dev_err(es305->dev, "invalid pdata\n");
+		ret = -EINVAL;
+		goto err_pdata;
+	} else if ((pdata->passthrough_src != 0) &&
+					(pdata->passthrough_dst != 0)) {
+		es305->passthrough = ((pdata->passthrough_src + 3) << 4) |
+					((pdata->passthrough_dst - 1) << 2);
+	}
+
+	ret = gpio_request(pdata->gpio_wakeup, "ES305 wakeup");
+	if (ret < 0) {
+		dev_err(es305->dev, "error requesting wakeup gpio\n");
+		goto err_gpio_wakeup;
+	}
+	gpio_direction_output(pdata->gpio_wakeup, 0);
+
+	ret = gpio_request(pdata->gpio_reset, "ES305 reset");
+	if (ret < 0) {
+		dev_err(es305->dev, "error requesting reset gpio\n");
+		goto err_gpio_reset;
+	}
+	gpio_direction_output(pdata->gpio_reset, 0);
+
+	mutex_init(&es305->lock);
+
+	request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+				ES305_FIRMWARE_NAME, es305->dev, GFP_KERNEL,
+				es305, es305_firmware_ready);
+
+	/*
+	 * This is not a critical failure, since the device can still be left
+	 * in passthrough mode.
+	 */
+	ret = sysfs_create_group(&es305->dev->kobj, &es305_attribute_group);
+	if (ret)
+		dev_err(es305->dev, "failed to create sysfs group\n");
+
+	return 0;
+
+err_gpio_reset:
+	gpio_free(pdata->gpio_wakeup);
+err_gpio_wakeup:
+err_pdata:
+	kfree(es305);
+err_kzalloc:
+	return ret;
+}
+
+static int __devexit es305_remove(struct i2c_client *client)
+{
+	struct es305_data *es305 = i2c_get_clientdata(client);
+
+	sysfs_remove_group(&es305->dev->kobj, &es305_attribute_group);
+
+	i2c_set_clientdata(client, NULL);
+
+	gpio_free(es305->pdata->gpio_wakeup);
+	gpio_free(es305->pdata->gpio_reset);
+	kfree(es305);
+
+	return 0;
+}
+
+static const struct i2c_device_id es305_id[] = {
+	{"audience_es305", 0},
+	{},
+};
+
+static struct i2c_driver es305_driver = {
+	.driver = {
+		.name = "audience_es305",
+		.owner = THIS_MODULE,
+	},
+	.probe = es305_probe,
+	.remove = __devexit_p(es305_remove),
+	.id_table = es305_id,
+};
+
+static int __init es305_init(void)
+{
+	return i2c_add_driver(&es305_driver);
+}
+
+static void __exit es305_exit(void)
+{
+	i2c_del_driver(&es305_driver);
+}
+
+module_init(es305_init);
+module_exit(es305_exit);
+
+MODULE_DESCRIPTION("Audience ES305 Voice Processor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/haptic_isa1200.c b/drivers/misc/haptic_isa1200.c
new file mode 100644
index 0000000..fe88ff03
--- /dev/null
+++ b/drivers/misc/haptic_isa1200.c
@@ -0,0 +1,286 @@
+/*
+ * haptic_isa1200.c - Haptic Controller
+ *
+ * Copyright (C) 2012 Samsung Electronics Co. Ltd. All Rights Reserved.
+ * Author: Vishnudev Ramakrishnan <vramakri@sta.samsung.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <../../../drivers/staging/android/timed_output.h>
+#include <linux/wakelock.h>
+#include <linux/platform_data/haptic_isa1200.h>
+
+#define ISA1200_HCTRL0		0x30
+#define ISA1200_HCTRL0_DEFAULT	1 /* power down mode, default */
+#define ISA1200_HCTRL0_HAPDREN			BIT(7)
+#define ISA1200_HCTRL0_HAPDIGMOD_PWM_IN		BIT(3)
+#define ISA1200_HCTRL0_PWMMOD_DIVIDER_128	0
+#define ISA1200_HCTRL0_PWMMOD_DIVIDER_256	1
+#define ISA1200_HCTRL0_PWMMOD_DIVIDER_512	2
+#define ISA1200_HCTRL0_PWMMOD_DIVIDER_1024	3
+
+#define ISA1200_HCTRL1		0x31
+#define ISA1200_HCTRL1_RESERVED_BIT6_ON		BIT(6)
+
+#define ISA1200_HCTRL1_MOTTYP_ERM	BIT(5)
+#define ISA1200_HCTRL1_MOTTYP_LRA	0
+
+#define PWM_HAPTIC_PERIOD	38676 /* 202 Hz with 128 divider */
+/* duty cycle for max vibration effect */
+#define PWM_MAX_VIBRATE_DUTY	(PWM_HAPTIC_PERIOD * 99/100)
+/* duty cycle for no vibration effect */
+#define PWM_NO_VIBRATE_DUTY	(PWM_HAPTIC_PERIOD * 50/100)
+
+struct isa1200_data {
+	struct i2c_client *client;
+	struct isa1200_platform_data *pdata;
+	struct hrtimer timer;
+	struct timed_output_dev dev;
+	struct mutex lock;
+	struct wake_lock wklock;
+	struct pwm_device *pwm;
+	bool vibrate;
+};
+
+static int isa1200_vibrate_on(struct isa1200_data *haptic)
+{
+	int ret;
+	u8 value;
+
+	pr_debug("vibrate is %d\n", haptic->vibrate);
+	if (!haptic->vibrate) {
+		wake_lock(&haptic->wklock);
+		ret = pwm_config(haptic->pwm, PWM_NO_VIBRATE_DUTY,
+				PWM_HAPTIC_PERIOD);
+		if (ret) {
+			pr_err("pwm_config failed %d\n", ret);
+			goto pwm_no_duty_cfg_failed;
+		}
+		pwm_enable(haptic->pwm);
+		gpio_set_value(haptic->pdata->hap_en_gpio, 1);
+		usleep_range(100, 200);
+		value = ISA1200_HCTRL0_HAPDREN |
+			ISA1200_HCTRL0_HAPDIGMOD_PWM_IN |
+			ISA1200_HCTRL0_PWMMOD_DIVIDER_128;
+		ret = i2c_smbus_write_byte_data(haptic->client, ISA1200_HCTRL0,
+						value);
+		if (ret < 0) {
+			pr_err("write HCTRL0 failed %d\n", ret);
+			goto err_return;
+		}
+		value = ISA1200_HCTRL1_RESERVED_BIT6_ON |
+			ISA1200_HCTRL1_MOTTYP_LRA;
+		ret = i2c_smbus_write_byte_data(haptic->client, ISA1200_HCTRL1,
+						value);
+		if (ret < 0) {
+			pr_err("write HCTRL1 failed %d\n", ret);
+			goto err_return;
+		}
+		ret = pwm_config(haptic->pwm, PWM_MAX_VIBRATE_DUTY,
+				PWM_HAPTIC_PERIOD);
+		if (ret) {
+			pr_err("pwm_config for max duty failed %d\n", ret);
+			goto err_return;
+		}
+		haptic->vibrate = true;
+	}
+	return 0;
+
+err_return:
+	gpio_set_value(haptic->pdata->hap_en_gpio, 0);
+	pwm_disable(haptic->pwm);
+pwm_no_duty_cfg_failed:
+	wake_unlock(&haptic->wklock);
+	return ret;
+}
+
+static void isa1200_vibrate_off(struct isa1200_data *haptic)
+{
+	int ret;
+	pr_debug("vibrate is %d\n", haptic->vibrate);
+	if (haptic->vibrate) {
+		ret = pwm_config(haptic->pwm, PWM_NO_VIBRATE_DUTY,
+				PWM_HAPTIC_PERIOD);
+		if (ret)
+			pr_err("pwm_config failed %d\n", ret);
+		gpio_set_value(haptic->pdata->hap_en_gpio, 0);
+		pwm_disable(haptic->pwm);
+		haptic->vibrate = false;
+		wake_unlock(&haptic->wklock);
+	}
+}
+
+static void isa1200_enable(struct timed_output_dev *dev, int timeout)
+{
+	int ret;
+	struct isa1200_data *haptic = container_of(dev, struct isa1200_data,
+					dev);
+
+	mutex_lock(&haptic->lock);
+	hrtimer_cancel(&haptic->timer);
+	pr_debug("timeout is %d msec\n", timeout);
+	if (timeout > 0) {
+		ret = isa1200_vibrate_on(haptic);
+		if (ret)
+			goto vibrate_error;
+		if (timeout > haptic->pdata->max_timeout)
+			timeout = haptic->pdata->max_timeout;
+		hrtimer_start(&haptic->timer,
+			ns_to_ktime((u64)timeout * NSEC_PER_MSEC),
+			HRTIMER_MODE_REL);
+	} else {
+		isa1200_vibrate_off(haptic);
+	}
+
+vibrate_error:
+	mutex_unlock(&haptic->lock);
+}
+
+static int isa1200_get_time(struct timed_output_dev *dev)
+{
+	struct isa1200_data *haptic = container_of(dev, struct isa1200_data,
+					dev);
+
+	if (hrtimer_active(&haptic->timer)) {
+		ktime_t r = hrtimer_get_remaining(&haptic->timer);
+		return ktime_to_ms(r);
+	}
+	return 0;
+}
+
+static enum hrtimer_restart isa1200_timer_func(struct hrtimer *timer)
+{
+	struct isa1200_data *haptic = container_of(timer, struct isa1200_data,
+					timer);
+	pr_debug("vibrate is %d\n", haptic->vibrate);
+	/* no lock required below, as isa1200_enable cancels timer first */
+	isa1200_vibrate_off(haptic);
+	return HRTIMER_NORESTART;
+}
+
+static int __devinit isa1200_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct isa1200_data *haptic;
+	struct isa1200_platform_data *pdata;
+	int ret;
+	pr_debug("\n");
+	if (!i2c_check_functionality(client->adapter,
+			I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) {
+		pr_err("no support for i2c write byte data\n");
+		return -ENOSYS;
+	}
+
+	pdata = client->dev.platform_data;
+	if (!pdata) {
+		pr_err("no platform data\n");
+		return -EINVAL;
+	}
+
+	haptic = kmalloc(sizeof(*haptic), GFP_KERNEL);
+	if (!haptic)
+		return -ENOMEM;
+	haptic->client = client;
+	haptic->pdata = pdata;
+	haptic->vibrate = false;
+
+	ret = gpio_request_one(pdata->hap_en_gpio, GPIOF_OUT_INIT_LOW,
+				"haptic_gpio");
+	if (ret) {
+		printk(KERN_ERR "%s: gpio %d request failed with error %d\n",
+			__func__, pdata->hap_en_gpio, ret);
+		goto enable_gpio_fail;
+	}
+
+	haptic->pwm = pwm_request(pdata->pwm_ch, id->name);
+	if (IS_ERR(haptic->pwm)) {
+		pr_err("pwm request failed\n");
+		ret = PTR_ERR(haptic->pwm);
+		goto pwm_req_fail;
+	}
+
+	mutex_init(&haptic->lock);
+	wake_lock_init(&haptic->wklock, WAKE_LOCK_SUSPEND, "vibrator");
+
+	hrtimer_init(&haptic->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	haptic->timer.function = isa1200_timer_func;
+
+	i2c_set_clientdata(client, haptic);
+
+	/* register with timed output class */
+	haptic->dev.name = "vibrator";
+	haptic->dev.get_time = isa1200_get_time;
+	haptic->dev.enable = isa1200_enable;
+	ret = timed_output_dev_register(&haptic->dev);
+	if (ret < 0) {
+		pr_err("timed output register failed %d\n", ret);
+		goto setup_fail;
+	}
+	pr_debug("%s registered\n", id->name);
+	return 0;
+
+setup_fail:
+	mutex_destroy(&haptic->lock);
+	wake_lock_destroy(&haptic->wklock);
+	pwm_free(haptic->pwm);
+pwm_req_fail:
+	gpio_free(pdata->hap_en_gpio);
+enable_gpio_fail:
+	kfree(haptic);
+	return ret;
+}
+
+static int __devexit isa1200_remove(struct i2c_client *client)
+{
+	struct isa1200_data *haptic = i2c_get_clientdata(client);
+	gpio_set_value(haptic->pdata->hap_en_gpio, 0);
+	timed_output_dev_unregister(&haptic->dev);
+	hrtimer_cancel(&haptic->timer);
+	mutex_destroy(&haptic->lock);
+	wake_lock_destroy(&haptic->wklock);
+	pwm_free(haptic->pwm);
+	gpio_free(haptic->pdata->hap_en_gpio);
+	kfree(haptic);
+	return 0;
+}
+
+static const struct i2c_device_id isa1200_id[] = {
+	{"isa1200", 0 },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, isa1200_id);
+
+static struct i2c_driver isa1200_driver = {
+	.driver = {
+		.name = "isa1200",
+		.owner = THIS_MODULE,
+	},
+	.probe = isa1200_probe,
+	.remove = __devexit_p(isa1200_remove),
+	.id_table = isa1200_id,
+};
+
+module_i2c_driver(isa1200_driver);
+
+MODULE_AUTHOR("Vishnudev Ramakrishnan <vramakri@sta.samsung.com>");
+MODULE_DESCRIPTION("ISA1200 Haptic Controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/sec_jack.c b/drivers/misc/sec_jack.c
new file mode 100755
index 0000000..4514dea
--- /dev/null
+++ b/drivers/misc/sec_jack.c
@@ -0,0 +1,505 @@
+/*  drivers/misc/sec_jack.c
+ *
+ *  Copyright (C) 2010 Samsung Electronics Co.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.
+ */
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/switch.h>
+#include <linux/input.h>
+#include <linux/timer.h>
+#include <linux/wakelock.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/sec_jack.h>
+
+#define MAX_ZONE_LIMIT		10
+#define SEND_KEY_CHECK_TIME_MS	30		/* 30ms */
+#define DET_CHECK_TIME_MS	200		/* 200ms */
+#define WAKE_LOCK_TIME		(HZ * 5)	/* 5 sec */
+
+struct sec_jack_info {
+	struct sec_jack_platform_data *pdata;
+	struct delayed_work jack_detect_work;
+	struct work_struct buttons_work;
+	struct work_struct detect_work;
+	struct workqueue_struct *queue;
+	struct input_dev *input_dev;
+	struct wake_lock det_wake_lock;
+	struct sec_jack_zone *zone;
+	struct input_handler handler;
+	struct input_handle handle;
+	struct input_device_id ids;
+	int det_irq;
+	int dev_id;
+	int pressed;
+	int pressed_code;
+	struct platform_device *send_key_dev;
+	unsigned int cur_jack_type;
+};
+
+/* with some modifications like moving all the gpio structs inside
+ * the platform data and getting the name for the switch and
+ * gpio_event from the platform data, the driver could support more than
+ * one headset jack, but currently user space is looking only for
+ * one key file and switch for a headset so it'd be overkill and
+ * untestable so we limit to one instantiation for now.
+ */
+static atomic_t instantiated = ATOMIC_INIT(0);
+
+/* sysfs name HeadsetObserver.java looks for to track headset state
+ */
+struct switch_dev switch_jack_detection = {
+	.name = "h2w",
+};
+
+static struct gpio_event_direct_entry sec_jack_key_map[] = {
+	{
+		.code	= KEY_UNKNOWN,
+	},
+};
+
+static struct gpio_event_input_info sec_jack_key_info = {
+	.info.func = gpio_event_input_func,
+	.info.no_suspend = true,
+	.type = EV_KEY,
+	.debounce_time.tv64 = SEND_KEY_CHECK_TIME_MS * NSEC_PER_MSEC,
+	.keymap = sec_jack_key_map,
+	.keymap_size = ARRAY_SIZE(sec_jack_key_map)
+};
+
+static struct gpio_event_info *sec_jack_input_info[] = {
+	&sec_jack_key_info.info,
+};
+
+static struct gpio_event_platform_data sec_jack_input_data = {
+	.name = "sec_jack",
+	.info = sec_jack_input_info,
+	.info_count = ARRAY_SIZE(sec_jack_input_info),
+};
+
+/* gpio_input driver does not support to read adc value.
+ * We use input filter to support 3-buttons of headset
+ * without changing gpio_input driver.
+ */
+static bool sec_jack_buttons_filter(struct input_handle *handle,
+				    unsigned int type, unsigned int code,
+				    int value)
+{
+	struct sec_jack_info *hi = handle->handler->private;
+
+	if (type != EV_KEY || code != KEY_UNKNOWN)
+		return false;
+
+	hi->pressed = value;
+
+	/* This is called in timer handler of gpio_input driver.
+	 * We use workqueue to read adc value.
+	 */
+	queue_work(hi->queue, &hi->buttons_work);
+
+	return true;
+}
+
+static int sec_jack_buttons_connect(struct input_handler *handler,
+				    struct input_dev *dev,
+				    const struct input_device_id *id)
+{
+	struct sec_jack_info *hi;
+	struct sec_jack_platform_data *pdata;
+	struct sec_jack_buttons_zone *btn_zones;
+	int err;
+	int i;
+
+	/* bind input_handler to input device related to only sec_jack */
+	if (dev->name != sec_jack_input_data.name)
+		return -ENODEV;
+
+	hi = handler->private;
+	pdata = hi->pdata;
+	btn_zones = pdata->buttons_zones;
+
+	hi->input_dev = dev;
+	hi->handle.dev = dev;
+	hi->handle.handler = handler;
+	hi->handle.open = 0;
+	hi->handle.name = "sec_jack_buttons";
+
+	err = input_register_handle(&hi->handle);
+	if (err) {
+		pr_err("%s: Failed to register sec_jack buttons handle, "
+			"error %d\n", __func__, err);
+		goto err_register_handle;
+	}
+
+	err = input_open_device(&hi->handle);
+	if (err) {
+		pr_err("%s: Failed to open input device, error %d\n",
+			__func__, err);
+		goto err_open_device;
+	}
+
+	for (i = 0; i < pdata->num_buttons_zones; i++)
+		input_set_capability(dev, EV_KEY, btn_zones[i].code);
+
+	return 0;
+
+ err_open_device:
+	input_unregister_handle(&hi->handle);
+ err_register_handle:
+
+	return err;
+}
+
+static void sec_jack_buttons_disconnect(struct input_handle *handle)
+{
+	input_close_device(handle);
+	input_unregister_handle(handle);
+}
+
+static void sec_jack_set_type(struct sec_jack_info *hi, int jack_type)
+{
+	struct sec_jack_platform_data *pdata = hi->pdata;
+
+	/* this can happen during slow inserts where we think we identified
+	 * the type but then we get another interrupt and do it again
+	 */
+	if (jack_type == hi->cur_jack_type) {
+		if (jack_type != SEC_HEADSET_4POLE)
+			pdata->set_micbias_state(false);
+		return;
+	}
+
+	if (jack_type == SEC_HEADSET_4POLE) {
+		/* for a 4 pole headset, enable detection of send/end key */
+		if (hi->send_key_dev == NULL)
+			/* enable to get events again */
+			hi->send_key_dev = platform_device_register_data(NULL,
+					GPIO_EVENT_DEV_NAME,
+					hi->dev_id,
+					&sec_jack_input_data,
+					sizeof(sec_jack_input_data));
+	} else {
+		/* for all other jacks, disable send/end key detection */
+		if (hi->send_key_dev != NULL) {
+			/* disable to prevent false events on next insert */
+			platform_device_unregister(hi->send_key_dev);
+			hi->send_key_dev = NULL;
+		}
+		/* micbias is left enabled for 4pole and disabled otherwise */
+		pdata->set_micbias_state(false);
+	}
+
+	hi->cur_jack_type = jack_type;
+	pr_info("%s : jack_type = %d\n", __func__, jack_type);
+
+	/* prevent suspend to allow user space to respond to switch */
+	wake_lock_timeout(&hi->det_wake_lock, WAKE_LOCK_TIME);
+
+	switch_set_state(&switch_jack_detection, jack_type);
+}
+
+static void handle_jack_not_inserted(struct sec_jack_info *hi)
+{
+	sec_jack_set_type(hi, SEC_JACK_NO_DEVICE);
+	hi->pdata->set_micbias_state(false);
+}
+
+static void determine_jack_type(struct sec_jack_info *hi)
+{
+	struct sec_jack_zone *zones = hi->pdata->zones;
+	int size = hi->pdata->num_zones;
+	int count[MAX_ZONE_LIMIT] = {0};
+	int adc;
+	int i;
+	unsigned npolarity = !hi->pdata->det_active_high;
+
+	while (gpio_get_value(hi->pdata->det_gpio) ^ npolarity) {
+		adc = hi->pdata->get_adc_value();
+		pr_debug("%s: adc = %d\n", __func__, adc);
+
+		/* determine the type of headset based on the
+		 * adc value.  An adc value can fall in various
+		 * ranges or zones.  Within some ranges, the type
+		 * can be returned immediately.  Within others, the
+		 * value is considered unstable and we need to sample
+		 * a few more types (up to the limit determined by
+		 * the range) before we return the type for that range.
+		 */
+		for (i = 0; i < size; i++) {
+			if (adc <= zones[i].adc_high) {
+				if (++count[i] > zones[i].check_count) {
+					sec_jack_set_type(hi,
+							  zones[i].jack_type);
+					return;
+				}
+				msleep(zones[i].delay_ms);
+				break;
+			}
+		}
+	}
+	/* jack removed before detection complete */
+	pr_debug("%s : jack removed before detection complete\n", __func__);
+	handle_jack_not_inserted(hi);
+}
+
+/* thread run whenever the headset detect state changes (either insertion
+ * or removal).
+ */
+static irqreturn_t sec_jack_detect_irq(int irq, void *dev_id)
+{
+	struct sec_jack_info *hi = dev_id;
+
+	queue_work(hi->queue, &hi->detect_work);
+
+	return IRQ_HANDLED;
+}
+
+void sec_jack_detect_work(struct work_struct *work)
+{
+	struct sec_jack_info *hi =
+		container_of(work, struct sec_jack_info, detect_work);
+	struct sec_jack_platform_data *pdata = hi->pdata;
+	int time_left_ms = DET_CHECK_TIME_MS;
+	unsigned npolarity = !hi->pdata->det_active_high;
+
+	/* set mic bias to enable adc */
+	pdata->set_micbias_state(true);
+
+	/* debounce headset jack.  don't try to determine the type of
+	 * headset until the detect state is true for a while.
+	 */
+	while (time_left_ms > 0) {
+		if (!(gpio_get_value(hi->pdata->det_gpio) ^ npolarity)) {
+			/* jack not detected. */
+			handle_jack_not_inserted(hi);
+			return;
+		}
+		msleep(10);
+		time_left_ms -= 10;
+	}
+	/* jack presence was detected the whole time, figure out which type */
+	determine_jack_type(hi);
+}
+
+/* thread run whenever the button of headset is pressed or released */
+void sec_jack_buttons_work(struct work_struct *work)
+{
+	struct sec_jack_info *hi =
+		container_of(work, struct sec_jack_info, buttons_work);
+	struct sec_jack_platform_data *pdata = hi->pdata;
+	struct sec_jack_buttons_zone *btn_zones = pdata->buttons_zones;
+	int adc;
+	int i;
+
+	/* when button is released */
+	if (hi->pressed == 0) {
+		input_report_key(hi->input_dev, hi->pressed_code, 0);
+		input_sync(hi->input_dev);
+		pr_debug("%s: keycode=%d, is released\n", __func__,
+			hi->pressed_code);
+		return;
+	}
+
+	/* when button is pressed */
+	adc = pdata->get_adc_value();
+
+	for (i = 0; i < pdata->num_buttons_zones; i++)
+		if (adc >= btn_zones[i].adc_low &&
+		    adc <= btn_zones[i].adc_high) {
+			hi->pressed_code = btn_zones[i].code;
+			input_report_key(hi->input_dev, btn_zones[i].code, 1);
+			input_sync(hi->input_dev);
+			pr_debug("%s: keycode=%d, is pressed\n", __func__,
+				btn_zones[i].code);
+			return;
+		}
+
+	pr_warn("%s: key is skipped. ADC value is %d\n", __func__, adc);
+}
+
+static int sec_jack_probe(struct platform_device *pdev)
+{
+	struct sec_jack_info *hi;
+	struct sec_jack_platform_data *pdata = pdev->dev.platform_data;
+	int ret;
+
+	pr_info("%s : Registering jack driver\n", __func__);
+	if (!pdata) {
+		pr_err("%s : pdata is NULL.\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!pdata->get_adc_value || !pdata->zones ||
+	    !pdata->set_micbias_state || pdata->num_zones > MAX_ZONE_LIMIT) {
+		pr_err("%s : need to check pdata\n", __func__);
+		return -ENODEV;
+	}
+
+	if (atomic_xchg(&instantiated, 1)) {
+		pr_err("%s : already instantiated, can only have one\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	sec_jack_key_map[0].gpio = pdata->send_end_gpio;
+
+	hi = kzalloc(sizeof(struct sec_jack_info), GFP_KERNEL);
+	if (hi == NULL) {
+		pr_err("%s : Failed to allocate memory.\n", __func__);
+		ret = -ENOMEM;
+		goto err_kzalloc;
+	}
+
+	hi->pdata = pdata;
+
+	/* make the id of our gpi_event device the same as our platform device,
+	 * which makes it the responsiblity of the board file to make sure
+	 * it is unique relative to other gpio_event devices
+	 */
+	hi->dev_id = pdev->id;
+
+	ret = gpio_request(pdata->det_gpio, "ear_jack_detect");
+	if (ret) {
+		pr_err("%s : gpio_request failed for %d\n",
+		       __func__, pdata->det_gpio);
+		goto err_gpio_request;
+	}
+
+	ret = switch_dev_register(&switch_jack_detection);
+	if (ret < 0) {
+		pr_err("%s : Failed to register switch device\n", __func__);
+		goto err_switch_dev_register;
+	}
+
+	wake_lock_init(&hi->det_wake_lock, WAKE_LOCK_SUSPEND, "sec_jack_det");
+
+	INIT_WORK(&hi->buttons_work, sec_jack_buttons_work);
+	INIT_WORK(&hi->detect_work, sec_jack_detect_work);
+	hi->queue = create_freezable_workqueue("sec_jack_wq");
+	if (hi->queue == NULL) {
+		ret = -ENOMEM;
+		pr_err("%s: Failed to create workqueue\n", __func__);
+		goto err_create_wq_failed;
+	}
+	queue_work(hi->queue, &hi->detect_work);
+
+	hi->det_irq = gpio_to_irq(pdata->det_gpio);
+
+	set_bit(EV_KEY, hi->ids.evbit);
+	hi->ids.flags = INPUT_DEVICE_ID_MATCH_EVBIT;
+	hi->handler.filter = sec_jack_buttons_filter;
+	hi->handler.connect = sec_jack_buttons_connect;
+	hi->handler.disconnect = sec_jack_buttons_disconnect;
+	hi->handler.name = "sec_jack_buttons";
+	hi->handler.id_table = &hi->ids;
+	hi->handler.private = hi;
+
+	ret = input_register_handler(&hi->handler);
+	if (ret) {
+		pr_err("%s : Failed to register_handler\n", __func__);
+		goto err_register_input_handler;
+	}
+	ret = request_irq(hi->det_irq, sec_jack_detect_irq,
+				   IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+				   IRQF_ONESHOT, "sec_headset_detect", hi);
+	if (ret) {
+		pr_err("%s : Failed to request_irq.\n", __func__);
+		goto err_request_detect_irq;
+	}
+
+	/* to handle insert/removal when we're sleeping in a call */
+	ret = enable_irq_wake(hi->det_irq);
+	if (ret) {
+		pr_err("%s : Failed to enable_irq_wake.\n", __func__);
+		goto err_enable_irq_wake;
+	}
+
+	dev_set_drvdata(&pdev->dev, hi);
+
+	return 0;
+
+err_enable_irq_wake:
+	free_irq(hi->det_irq, hi);
+err_request_detect_irq:
+	input_unregister_handler(&hi->handler);
+err_register_input_handler:
+	destroy_workqueue(hi->queue);
+err_create_wq_failed:
+	wake_lock_destroy(&hi->det_wake_lock);
+	switch_dev_unregister(&switch_jack_detection);
+err_switch_dev_register:
+	gpio_free(pdata->det_gpio);
+err_gpio_request:
+	kfree(hi);
+err_kzalloc:
+	atomic_set(&instantiated, 0);
+
+	return ret;
+}
+
+static int sec_jack_remove(struct platform_device *pdev)
+{
+
+	struct sec_jack_info *hi = dev_get_drvdata(&pdev->dev);
+
+	pr_info("%s :\n", __func__);
+	disable_irq_wake(hi->det_irq);
+	free_irq(hi->det_irq, hi);
+	destroy_workqueue(hi->queue);
+	if (hi->send_key_dev) {
+		platform_device_unregister(hi->send_key_dev);
+		hi->send_key_dev = NULL;
+	}
+	input_unregister_handler(&hi->handler);
+	wake_lock_destroy(&hi->det_wake_lock);
+	switch_dev_unregister(&switch_jack_detection);
+	gpio_free(hi->pdata->det_gpio);
+	kfree(hi);
+	atomic_set(&instantiated, 0);
+
+	return 0;
+}
+
+static struct platform_driver sec_jack_driver = {
+	.probe = sec_jack_probe,
+	.remove = sec_jack_remove,
+	.driver = {
+			.name = "sec_jack",
+			.owner = THIS_MODULE,
+		   },
+};
+static int __init sec_jack_init(void)
+{
+	return platform_driver_register(&sec_jack_driver);
+}
+
+static void __exit sec_jack_exit(void)
+{
+	platform_driver_unregister(&sec_jack_driver);
+}
+
+module_init(sec_jack_init);
+module_exit(sec_jack_exit);
+
+MODULE_AUTHOR("ms17.kim@samsung.com");
+MODULE_DESCRIPTION("Samsung Electronics Corp Ear-Jack detection driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/stmpe811-adc.c b/drivers/misc/stmpe811-adc.c
new file mode 100644
index 0000000..14b14d7
--- /dev/null
+++ b/drivers/misc/stmpe811-adc.c
@@ -0,0 +1,285 @@
+/*
+ * stmpe811-adc.c
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * SangYoung Son <hello.son@samsung.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 and
+ * only 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/fs.h>
+#include <linux/gpio.h>
+#include <linux/platform_data/stmpe811-adc.h>
+
+#define STMPE811_CHIP_ID	0x00
+#define STMPE811_ID_VER		0x02
+#define STMPE811_SYS_CTRL1	0x03
+#define STMPE811_SYS_CTRL2	0x04
+#define STMPE811_INT_CTRL	0x09
+#define STMPE811_INT_EN		0x0A
+#define STMPE811_INT_STA	0x0B
+#define STMPE811_ADC_INT_EN	0x0E
+#define STMPE811_ADC_INT_STA	0x0F
+#define STMPE811_ADC_CTRL1	0x20
+#define STMPE811_ADC_CTRL2	0x21
+#define STMPE811_ADC_CAPT	0x22
+#define STMPE811_ADC_DATA_CH0	0x30
+#define STMPE811_ADC_DATA_CH1	0x32
+#define STMPE811_ADC_DATA_CH2	0x34
+#define STMPE811_ADC_DATA_CH3	0x36
+#define STMPE811_ADC_DATA_CH4	0x38
+#define STMPE811_ADC_DATA_CH5	0x3A
+#define STMPE811_ADC_DATA_CH6	0x3C
+#define STMPE811_ADC_DATA_CH7	0x3E
+#define STMPE811_GPIO_AF	0x17
+#define STMPE811_TSC_CTRL	0x40
+
+static struct i2c_client *stmpe811_adc_i2c_client;
+
+struct stmpe811_adc_data {
+	struct i2c_client	*client;
+	struct stmpe811_platform_data	*pdata;
+	struct stmpe811_callbacks		callbacks;
+
+	struct mutex		adc_lock;
+};
+
+static int stmpe811_i2c_read(struct i2c_client *client, u8 reg, u8 *data,
+			     u8 length)
+{
+	int ret;
+
+	ret = i2c_smbus_read_i2c_block_data(client, reg, length, data);
+	if (ret != length) {
+		dev_err(&client->dev, "%s: err %d, reg: 0x%02x\n", __func__,
+			ret, reg);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int stmpe811_write_register(struct i2c_client *client, u8 reg,
+				   u16 w_data)
+{
+	int ret;
+
+	ret = i2c_smbus_write_word_data(client, reg, w_data);
+	if (ret < 0) {
+		dev_err(&client->dev, "%s: err %d, reg: 0x%02x\n", __func__,
+			ret, reg);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int stmpe811_get_adc_data(u8 channel)
+{
+	struct i2c_client *client = stmpe811_adc_i2c_client;
+	u8 data[2];
+	u16 w_data;
+	u8 data_channel_addr;
+	int ret;
+
+	ret = stmpe811_write_register(client, STMPE811_ADC_CAPT,
+				      (1 << channel));
+	if (ret < 0)
+		return ret;
+
+	msleep(20);
+	data_channel_addr = STMPE811_ADC_DATA_CH0 + (channel * 2);
+	ret = stmpe811_i2c_read(client, data_channel_addr, data, 2);
+	if (ret < 0)
+		return ret;
+
+	w_data = ((data[0]<<8) | data[1]) & 0x0FFF;
+	pr_debug("%s: STMPE811_ADC_DATA_CH%d(0x%x, %d)\n", __func__,
+				channel, w_data, w_data);
+
+	return w_data;
+}
+
+static int stmpe811_reg_init(struct stmpe811_adc_data *adc_data)
+{
+	struct i2c_client *client = adc_data->client;
+	int ret;
+
+	/* clock control: only adc on */
+	ret = stmpe811_write_register(client, STMPE811_SYS_CTRL2, 0x0E);
+	if (ret < 0)
+		goto reg_init_error;
+
+	/* interrupt enable: disable interrupt */
+	ret = stmpe811_write_register(client, STMPE811_INT_EN, 0x00);
+	if (ret < 0)
+		goto reg_init_error;
+
+	/* adc control: 64 sample time, 12bit adc, internel referance*/
+	ret = stmpe811_write_register(client, STMPE811_ADC_CTRL1, 0x38);
+	if (ret < 0)
+		goto reg_init_error;
+
+	/* adc control: 1.625MHz typ */
+	ret = stmpe811_write_register(client, STMPE811_ADC_CTRL2, 0x03);
+	if (ret < 0)
+		goto reg_init_error;
+
+	/* alt func: use for adc */
+	ret = stmpe811_write_register(client, STMPE811_GPIO_AF, 0x00);
+	if (ret < 0)
+		goto reg_init_error;
+
+	/* ts control: tsc disable */
+	ret = stmpe811_write_register(client, STMPE811_TSC_CTRL, 0x00);
+	if (ret < 0)
+		goto reg_init_error;
+
+	return 0;
+
+reg_init_error:
+	dev_err(&client->dev, "%s: reg init error: %d\n", __func__, ret);
+	return ret;
+}
+
+static const struct file_operations stmpe811_fops = {
+	.owner = THIS_MODULE,
+};
+
+static struct miscdevice stmpe811_adc_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "sec_adc",
+	.fops = &stmpe811_fops,
+};
+
+static int __devinit stmpe811_adc_i2c_probe(struct i2c_client *client,
+					const struct i2c_device_id *id)
+{
+	struct stmpe811_platform_data *pdata = client->dev.platform_data;
+	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+	struct stmpe811_adc_data *adc_data;
+	int ret;
+	u8 i2c_data[2];
+	u8 rev[2];
+
+	if (pdata == NULL) {
+		dev_err(&client->dev, "%s: no pdata\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
+		return -EIO;
+
+	adc_data = kzalloc(sizeof(*adc_data), GFP_KERNEL);
+	if (!adc_data)
+		return -ENOMEM;
+
+	adc_data->client = client;
+	adc_data->pdata = pdata;
+	adc_data->callbacks.get_adc_data = stmpe811_get_adc_data;
+	if (pdata->register_cb)
+		pdata->register_cb(&adc_data->callbacks);
+
+	i2c_set_clientdata(client, adc_data);
+	stmpe811_adc_i2c_client = client;
+
+	ret = misc_register(&stmpe811_adc_device);
+	if (ret)
+		goto misc_register_fail;
+
+	mutex_init(&adc_data->adc_lock);
+
+	/* initialize adc registers */
+	ret = stmpe811_reg_init(adc_data);
+	if (ret < 0)
+		goto reg_init_error;
+
+	/* TODO: ADC_INT setting */
+
+	ret = stmpe811_i2c_read(client, STMPE811_CHIP_ID, i2c_data, 2);
+	if (ret < 0)
+		goto reg_init_error;
+	/* read revision number, 0x01 for es, 0x03 for final silicon */
+	ret = stmpe811_i2c_read(client, STMPE811_ID_VER, rev, 2);
+	if (ret < 0)
+		goto reg_init_error;
+
+	dev_info(&client->dev, "stmpe811 adc (id 0x%x rev 0x%x)\n",
+		 ((i2c_data[0]<<8) | i2c_data[1]), rev[0]);
+	return 0;
+
+reg_init_error:
+	mutex_destroy(&adc_data->adc_lock);
+	misc_deregister(&stmpe811_adc_device);
+misc_register_fail:
+	if (pdata->register_cb)
+		pdata->register_cb(NULL);
+
+	dev_err(&client->dev, "stmpe811 probe fail: %d\n", ret);
+	kfree(adc_data);
+	return ret;
+}
+
+static int __devexit stmpe811_adc_i2c_remove(struct i2c_client *client)
+{
+	struct stmpe811_adc_data *adc = i2c_get_clientdata(client);
+
+	misc_deregister(&stmpe811_adc_device);
+	mutex_destroy(&adc->adc_lock);
+	kfree(adc);
+
+	return 0;
+}
+
+static const struct i2c_device_id stmpe811_adc_device_id[] = {
+	{"stmpe811-adc", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, stmpe811_adc_device_id);
+
+static struct i2c_driver stmpe811_adc_i2c_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "stmpe811-adc",
+	},
+	.probe		= stmpe811_adc_i2c_probe,
+	.remove		= stmpe811_adc_i2c_remove,
+	.id_table	= stmpe811_adc_device_id,
+};
+
+static int __init stmpe811_adc_init(void)
+{
+	return i2c_add_driver(&stmpe811_adc_i2c_driver);
+}
+
+static void __exit stmpe811_adc_exit(void)
+{
+	i2c_del_driver(&stmpe811_adc_i2c_driver);
+}
+
+module_init(stmpe811_adc_init);
+module_exit(stmpe811_adc_exit);
+
+MODULE_AUTHOR("SangYoung Son <hello.son@samsung.com>");
+MODULE_DESCRIPTION("stmpe811 adc driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 7d06985..117b98a 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -58,13 +58,6 @@
 #define INAND_CMD38_ARG_SECTRIM1 0x81
 #define INAND_CMD38_ARG_SECTRIM2 0x88
 
-#define mmc_req_rel_wr(req)	(((req->cmd_flags & REQ_FUA) || \
-			(req->cmd_flags & REQ_META)) && \
-			(rq_data_dir(req) == WRITE))
-#define PACKED_CMD_VER		0x01
-#define PACKED_CMD_RD		0x01
-#define PACKED_CMD_WR		0x02
-
 static DEFINE_MUTEX(block_mutex);
 
 /*
@@ -105,7 +98,6 @@
 #define MMC_BLK_WRITE		BIT(1)
 #define MMC_BLK_DISCARD		BIT(2)
 #define MMC_BLK_SECDISCARD	BIT(3)
-#define MMC_BLK_WR_HDR		BIT(4)
 
 	/*
 	 * Only set in main mmc_blk_data associated
@@ -131,24 +123,9 @@
 	MMC_BLK_NOMEDIUM,
 };
 
-enum {
-	MMC_PACKED_N_IDX = -1,
-	MMC_PACKED_N_ZERO,
-	MMC_PACKED_N_SINGLE,
-};
-
 module_param(perdev_minors, int, 0444);
 MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device");
 
-static inline void mmc_blk_clear_packed(struct mmc_queue_req *mqrq)
-{
-	mqrq->packed_cmd = MMC_PACKED_NONE;
-	mqrq->packed_num = MMC_PACKED_N_ZERO;
-	mqrq->packed_sg_flag = MMC_PACKED_NONE_SG;
-	mqrq->mmc_active.__mrq = NULL;
-	mqrq->mmc_active.__cond = false;
-}
-
 static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
 {
 	struct mmc_blk_data *md;
@@ -1072,8 +1049,7 @@
 	 * kind.  If it was a write, we may have transitioned to
 	 * program mode, which we have to wait for it to complete.
 	 */
-	if ((!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) ||
-			(mq_mrq->packed_cmd == MMC_PACKED_WR_HDR)) {
+	if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
 		u32 status;
 		do {
 			int err = get_card_status(card, &status, 5);
@@ -1098,8 +1074,7 @@
 		       (unsigned)blk_rq_sectors(req),
 		       brq->cmd.resp[0], brq->stop.resp[0]);
 
-		if (rq_data_dir(req) == READ &&
-				mq_mrq->packed_cmd != MMC_PACKED_WR_HDR) {
+		if (rq_data_dir(req) == READ) {
 			if (ecc_err)
 				return MMC_BLK_ECC_ERR;
 			return MMC_BLK_DATA_ERR;
@@ -1111,61 +1086,12 @@
 	if (!brq->data.bytes_xfered)
 		return MMC_BLK_RETRY;
 
-	if (mq_mrq->packed_cmd != MMC_PACKED_NONE) {
-		if (unlikely(brq->data.blocks << 9 != brq->data.bytes_xfered))
-			return MMC_BLK_PARTIAL;
-		else
-			return MMC_BLK_SUCCESS;
-	}
-
 	if (blk_rq_bytes(req) != brq->data.bytes_xfered)
 		return MMC_BLK_PARTIAL;
 
 	return MMC_BLK_SUCCESS;
 }
 
-static int mmc_blk_packed_err_check(struct mmc_card *card,
-				    struct mmc_async_req *areq)
-{
-	struct mmc_queue_req *mq_rq = container_of(areq, struct mmc_queue_req,
-			mmc_active);
-	struct request *req = mq_rq->req;
-	int err, check, status;
-	u8 ext_csd[512];
-
-	mq_rq->packed_retries--;
-	check = mmc_blk_err_check(card, areq);
-	err = get_card_status(card, &status, 0);
-	if (err) {
-		pr_err("%s: error %d sending status command\n",
-				req->rq_disk->disk_name, err);
-		return MMC_BLK_ABORT;
-	}
-
-	if (status & R1_EXP_EVENT) {
-		err = mmc_send_ext_csd(card, ext_csd);
-		if (err) {
-			pr_err("%s: error %d sending ext_csd\n",
-					req->rq_disk->disk_name, err);
-			return MMC_BLK_ABORT;
-		}
-
-		if ((ext_csd[EXT_CSD_EXP_EVENTS_STATUS] &
-					EXT_CSD_PACKED_FAILURE) &&
-				(ext_csd[EXT_CSD_PACKED_CMD_STATUS] &
-				 EXT_CSD_PACKED_GENERIC_ERROR)) {
-			if (ext_csd[EXT_CSD_PACKED_CMD_STATUS] &
-					EXT_CSD_PACKED_INDEXED_ERROR) {
-				mq_rq->packed_fail_idx =
-				  ext_csd[EXT_CSD_PACKED_FAILURE_INDEX] - 1;
-				return MMC_BLK_PARTIAL;
-			}
-		}
-	}
-
-	return check;
-}
-
 static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
 			       struct mmc_card *card,
 			       int disable_multi,
@@ -1320,274 +1246,10 @@
 	mmc_queue_bounce_pre(mqrq);
 }
 
-static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
-{
-	struct request_queue *q = mq->queue;
-	struct mmc_card *card = mq->card;
-	struct request *cur = req, *next = NULL;
-	struct mmc_blk_data *md = mq->data;
-	bool en_rel_wr = card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN;
-	unsigned int req_sectors = 0, phys_segments = 0;
-	unsigned int max_blk_count, max_phys_segs;
-	u8 put_back = 0;
-	u8 max_packed_rw = 0;
-	u8 reqs = 0;
-
-	mmc_blk_clear_packed(mq->mqrq_cur);
-
-	if (!(md->flags & MMC_BLK_CMD23) ||
-			!card->ext_csd.packed_event_en)
-		goto no_packed;
-
-	if ((rq_data_dir(cur) == WRITE) &&
-			(card->host->caps2 & MMC_CAP2_PACKED_WR))
-		max_packed_rw = card->ext_csd.max_packed_writes;
-	else if ((rq_data_dir(cur) == READ) &&
-			(card->host->caps2 & MMC_CAP2_PACKED_RD))
-		max_packed_rw = card->ext_csd.max_packed_reads;
-
-	if (max_packed_rw == 0)
-		goto no_packed;
-
-	if (mmc_req_rel_wr(cur) &&
-			(md->flags & MMC_BLK_REL_WR) &&
-			!en_rel_wr)
-		goto no_packed;
-
-	max_blk_count = min(card->host->max_blk_count,
-			card->host->max_req_size >> 9);
-	if (unlikely(max_blk_count > 0xffff))
-		max_blk_count = 0xffff;
-
-	max_phys_segs = queue_max_segments(q);
-	req_sectors += blk_rq_sectors(cur);
-	phys_segments += cur->nr_phys_segments;
-
-	if (rq_data_dir(cur) == WRITE) {
-		req_sectors++;
-		phys_segments++;
-	}
-
-	while (reqs < max_packed_rw - 1) {
-		spin_lock_irq(q->queue_lock);
-		next = blk_fetch_request(q);
-		spin_unlock_irq(q->queue_lock);
-		if (!next)
-			break;
-
-		if (next->cmd_flags & REQ_DISCARD ||
-				next->cmd_flags & REQ_FLUSH) {
-			put_back = 1;
-			break;
-		}
-
-		if (rq_data_dir(cur) != rq_data_dir(next)) {
-			put_back = 1;
-			break;
-		}
-
-		if (mmc_req_rel_wr(next) &&
-				(md->flags & MMC_BLK_REL_WR) &&
-				!en_rel_wr) {
-			put_back = 1;
-			break;
-		}
-
-		req_sectors += blk_rq_sectors(next);
-		if (req_sectors > max_blk_count) {
-			put_back = 1;
-			break;
-		}
-
-		phys_segments +=  next->nr_phys_segments;
-		if (phys_segments > max_phys_segs) {
-			put_back = 1;
-			break;
-		}
-
-		list_add_tail(&next->queuelist, &mq->mqrq_cur->packed_list);
-		cur = next;
-		reqs++;
-	}
-
-	if (put_back) {
-		spin_lock_irq(q->queue_lock);
-		blk_requeue_request(q, next);
-		spin_unlock_irq(q->queue_lock);
-	}
-
-	if (reqs > 0) {
-		list_add(&req->queuelist, &mq->mqrq_cur->packed_list);
-		mq->mqrq_cur->packed_num = ++reqs;
-		mq->mqrq_cur->packed_retries = reqs;
-		return reqs;
-	}
-
-no_packed:
-	mmc_blk_clear_packed(mq->mqrq_cur);
-	return 0;
-}
-
-static void mmc_blk_packed_rrq_prep(struct mmc_queue_req *mqrq,
-			       struct mmc_card *card,
-			       struct mmc_queue *mq)
-{
-	struct mmc_queue_req *__mqrq = mq->mqrq_hdr;
-	struct mmc_blk_request *brq = &mqrq->brq;
-	struct request *req = mqrq->req;
-
-	mqrq->packed_cmd = MMC_PACKED_READ;
-	mqrq->packed_sg_flag = MMC_PACKED_RD_SG;
-
-	memset(brq, 0, sizeof(struct mmc_blk_request));
-	brq->mrq.cmd = &brq->cmd;
-	brq->mrq.data = &brq->data;
-	brq->mrq.stop = &brq->stop;
-
-	brq->cmd.opcode = MMC_READ_MULTIPLE_BLOCK;
-	brq->cmd.arg = blk_rq_pos(req);
-	if (!mmc_card_blockaddr(card))
-		brq->cmd.arg <<= 9;
-	brq->cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
-	brq->data.blksz = 512;
-	brq->data.blocks = mqrq->packed_blocks;
-	brq->data.flags |= MMC_DATA_READ;
-
-	brq->stop.opcode = MMC_STOP_TRANSMISSION;
-	brq->stop.arg = 0;
-	brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
-
-	mmc_set_data_timeout(&brq->data, card);
-
-	brq->data.sg = mqrq->sg;
-	brq->data.sg_len = mmc_queue_map_sg(mq, mqrq);
-
-	mqrq->mmc_active.mrq = &brq->mrq;
-	mqrq->mmc_active.__mrq = NULL;
-	mqrq->mmc_active.__cond = true;
-	mqrq->mmc_active.err_check = mmc_blk_packed_err_check;
-	__mqrq->mmc_active.__mrq = &brq->mrq;
-}
-
-static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
-					struct mmc_card *card,
-					struct mmc_queue *mq)
-{
-	struct mmc_queue_req *__mqrq;
-	struct mmc_blk_request *brq;
-	struct request *req = mqrq->req;
-	struct request *prq;
-	struct mmc_blk_data *md = mq->data;
-	bool do_rel_wr, do_data_tag;
-	u32 *packed_cmd_hdr;
-	u8 i = 1;
-
-	if (rq_data_dir(req) == READ) {
-		__mqrq = mq->mqrq_hdr;
-		__mqrq->packed_cmd = MMC_PACKED_WR_HDR;
-		__mqrq->packed_sg_flag = MMC_PACKED_HDR_SG;
-		__mqrq->req = mqrq->req;
-	} else {
-		__mqrq = mqrq;
-		__mqrq->packed_cmd = MMC_PACKED_WRITE;
-		__mqrq->packed_sg_flag = MMC_PACKED_WR_SG;
-	}
-
-	brq = &__mqrq->brq;
-	packed_cmd_hdr = __mqrq->packed_cmd_hdr;
-
-	mqrq->packed_blocks = 0;
-	mqrq->packed_fail_idx = MMC_PACKED_N_IDX;
-
-	memset(packed_cmd_hdr, 0, sizeof(__mqrq->packed_cmd_hdr));
-	packed_cmd_hdr[0] = (mqrq->packed_num << 16) |
-		(((rq_data_dir(req) == READ) ?
-		  PACKED_CMD_RD : PACKED_CMD_WR) << 8) |
-		PACKED_CMD_VER;
-
-	/*
-	 * Argument for each entry of packed group
-	 */
-	list_for_each_entry(prq, &mqrq->packed_list, queuelist) {
-		do_rel_wr = mmc_req_rel_wr(prq) && (md->flags & MMC_BLK_REL_WR);
-		do_data_tag = (card->ext_csd.data_tag_unit_size) &&
-			(prq->cmd_flags & REQ_META) &&
-			(rq_data_dir(prq) == WRITE) &&
-			((brq->data.blocks * brq->data.blksz) >=
-			 card->ext_csd.data_tag_unit_size);
-		/* Argument of CMD23 */
-		packed_cmd_hdr[(i * 2)] =
-			(do_rel_wr ? MMC_CMD23_ARG_REL_WR : 0) |
-			(do_data_tag ? MMC_CMD23_ARG_TAG_REQ : 0) |
-			blk_rq_sectors(prq);
-		/* Argument of CMD18 or CMD25 */
-		packed_cmd_hdr[((i * 2)) + 1] =
-			mmc_card_blockaddr(card) ?
-			blk_rq_pos(prq) : blk_rq_pos(prq) << 9;
-		mqrq->packed_blocks += blk_rq_sectors(prq);
-		i++;
-	}
-
-	memset(brq, 0, sizeof(struct mmc_blk_request));
-	brq->mrq.cmd = &brq->cmd;
-	brq->mrq.data = &brq->data;
-	brq->mrq.sbc = &brq->sbc;
-	brq->mrq.stop = &brq->stop;
-
-	brq->sbc.opcode = MMC_SET_BLOCK_COUNT;
-	brq->sbc.arg = MMC_CMD23_ARG_PACKED |
-		((rq_data_dir(req) == READ) ? 1 : mqrq->packed_blocks + 1);
-	brq->sbc.flags = MMC_RSP_R1 | MMC_CMD_AC;
-
-	brq->cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK;
-	brq->cmd.arg = blk_rq_pos(req);
-	if (!mmc_card_blockaddr(card))
-		brq->cmd.arg <<= 9;
-	brq->cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
-
-	brq->data.blksz = 512;
-	/*
-	 * Write separately the packd command header only for packed read.
-	 * In case of packed write, header is sent with blocks of data.
-	 */
-	brq->data.blocks = (rq_data_dir(req) == READ) ?
-		1 : mqrq->packed_blocks + 1;
-	brq->data.flags |= MMC_DATA_WRITE;
-
-	brq->stop.opcode = MMC_STOP_TRANSMISSION;
-	brq->stop.arg = 0;
-	brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
-
-	mmc_set_data_timeout(&brq->data, card);
-
-	brq->data.sg = __mqrq->sg;
-	brq->data.sg_len = mmc_queue_map_sg(mq, __mqrq);
-
-	__mqrq->mmc_active.mrq = &brq->mrq;
-
-	if (rq_data_dir(req) == READ)
-		__mqrq->mmc_active.err_check = mmc_blk_err_check;
-	else
-		__mqrq->mmc_active.err_check = mmc_blk_packed_err_check;
-
-	mmc_queue_bounce_pre(__mqrq);
-
-	/*
-	 * In case of packed read, header is split with data.
-	 * Prepare the data ahead for packed read.
-	 * This will preserve the asynchronos transfer.
-	 */
-	if (rq_data_dir(req) == READ)
-		mmc_blk_packed_rrq_prep(mqrq, card, mq);
-}
-
 static int mmc_blk_cmd_err(struct mmc_blk_data *md, struct mmc_card *card,
 			   struct mmc_blk_request *brq, struct request *req,
 			   int ret)
 {
-	struct mmc_queue_req *mq_rq;
-	mq_rq = container_of(brq, struct mmc_queue_req, brq);
-
 	/*
 	 * If this is an SD card and we're writing, we can first
 	 * mark the known good sectors as ok.
@@ -1606,132 +1268,11 @@
 			spin_unlock_irq(&md->lock);
 		}
 	} else {
-		if (mq_rq->packed_cmd == MMC_PACKED_NONE) {
-			spin_lock_irq(&md->lock);
-			ret = __blk_end_request(req, 0, brq->data.bytes_xfered);
-			spin_unlock_irq(&md->lock);
-		}
-	}
-	return ret;
-}
-
-static int mmc_blk_end_packed_req(struct mmc_queue *mq,
-		struct mmc_queue_req *mq_rq)
-{
-	struct mmc_blk_data *md = mq->data;
-	struct request *prq;
-	int idx = mq_rq->packed_fail_idx, i = 0;
-	int ret = 0;
-
-	while (!list_empty(&mq_rq->packed_list)) {
-		prq = list_entry_rq(mq_rq->packed_list.next);
-		if (idx == i) {
-			/* retry from error index */
-			mq_rq->packed_num -= idx;
-			mq_rq->req = prq;
-			ret = 1;
-
-			if (mq_rq->packed_num == MMC_PACKED_N_SINGLE) {
-				list_del_init(&prq->queuelist);
-				mmc_blk_clear_packed(mq_rq);
-			}
-			return ret;
-		}
-		list_del_init(&prq->queuelist);
 		spin_lock_irq(&md->lock);
-		__blk_end_request(prq, 0, blk_rq_bytes(prq));
-		spin_unlock_irq(&md->lock);
-		i++;
-	}
-
-	mmc_blk_clear_packed(mq_rq);
-	return ret;
-}
-
-static int mmc_blk_chk_hdr_err(struct mmc_queue *mq, int status)
-{
-	struct mmc_blk_data *md = mq->data;
-	struct mmc_card *card = md->queue.card;
-	int type = MMC_BLK_WR_HDR, err = 0;
-
-	switch (status) {
-	case MMC_BLK_PARTIAL:
-	case MMC_BLK_RETRY:
-		err = 0;
-		break;
-	case MMC_BLK_CMD_ERR:
-	case MMC_BLK_ABORT:
-	case MMC_BLK_DATA_ERR:
-	case MMC_BLK_ECC_ERR:
-		err = mmc_blk_reset(md, card->host, type);
-		if (!err)
-			mmc_blk_reset_success(md, type);
-		break;
-	}
-
-	return err;
-}
-
-static int mmc_blk_issue_packed_rd(struct mmc_queue *mq,
-		struct mmc_queue_req *mq_rq)
-{
-	struct mmc_blk_data *md = mq->data;
-	struct mmc_card *card = md->queue.card;
-	int status, ret = -EIO, retry = 2;
-
-	do {
-		mmc_start_req(card->host, &mq_rq->mmc_active, &status);
-		if (!status) {
-			ret = 0;
-			break;
-		}
-
-		ret = mmc_blk_chk_hdr_err(mq, status);
-		if (ret)
-			break;
-		mmc_blk_packed_hdr_wrq_prep(mq_rq, card, mq);
-		mmc_start_req(card->host, &mq->mqrq_hdr->mmc_active, NULL);
-	} while (retry-- > 0);
-
-	return ret;
-}
-
-static void mmc_blk_abort_packed_req(struct mmc_queue *mq,
-		struct mmc_queue_req *mq_rq)
-{
-	struct mmc_blk_data *md = mq->data;
-	struct request *prq;
-
-	while (!list_empty(&mq_rq->packed_list)) {
-		prq = list_entry_rq(mq_rq->packed_list.next);
-		list_del_init(&prq->queuelist);
-		spin_lock_irq(&md->lock);
-		__blk_end_request(prq, -EIO, blk_rq_bytes(prq));
+		ret = __blk_end_request(req, 0, brq->data.bytes_xfered);
 		spin_unlock_irq(&md->lock);
 	}
-
-	mmc_blk_clear_packed(mq_rq);
-}
-
-static void mmc_blk_revert_packed_req(struct mmc_queue *mq,
-				      struct mmc_queue_req *mq_rq)
-{
-	struct request *prq;
-	struct request_queue *q = mq->queue;
-
-	while (!list_empty(&mq_rq->packed_list)) {
-		prq = list_entry_rq(mq_rq->packed_list.prev);
-		if (prq->queuelist.prev != &mq_rq->packed_list) {
-			list_del_init(&prq->queuelist);
-			spin_lock_irq(q->queue_lock);
-			blk_requeue_request(mq->queue, prq);
-			spin_unlock_irq(q->queue_lock);
-		} else {
-			list_del_init(&prq->queuelist);
-		}
-	}
-
-	mmc_blk_clear_packed(mq_rq);
+	return ret;
 }
 
 static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
@@ -1744,37 +1285,19 @@
 	struct mmc_queue_req *mq_rq;
 	struct request *req;
 	struct mmc_async_req *areq;
-	const u8 packed_num = 2;
-	u8 reqs = 0;
 
 	if (!rqc && !mq->mqrq_prev->req)
 		return 0;
 
-	if (rqc)
-		reqs = mmc_blk_prep_packed_list(mq, rqc);
-
 	do {
 		if (rqc) {
-			if (reqs >= packed_num)
-				mmc_blk_packed_hdr_wrq_prep(mq->mqrq_cur,
-						card, mq);
-			else
-				mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
-
-			if (mq->mqrq_cur->packed_cmd == MMC_PACKED_READ)
-				areq = &mq->mqrq_hdr->mmc_active;
-			else
-				areq = &mq->mqrq_cur->mmc_active;
+			mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
+			areq = &mq->mqrq_cur->mmc_active;
 		} else
 			areq = NULL;
-
 		areq = mmc_start_req(card->host, areq, (int *) &status);
-		if (!areq) {
-			if (mq->mqrq_cur->packed_cmd == MMC_PACKED_READ)
-				goto snd_packed_rd;
-			else
-				return 0;
-		}
+		if (!areq)
+			return 0;
 
 		mq_rq = container_of(areq, struct mmc_queue_req, mmc_active);
 		brq = &mq_rq->brq;
@@ -1789,16 +1312,10 @@
 			 * A block was successfully transferred.
 			 */
 			mmc_blk_reset_success(md, type);
-
-			if (mq_rq->packed_cmd != MMC_PACKED_NONE) {
-				ret = mmc_blk_end_packed_req(mq, mq_rq);
-				break;
-			} else {
-				spin_lock_irq(&md->lock);
-				ret = __blk_end_request(req, 0,
+			spin_lock_irq(&md->lock);
+			ret = __blk_end_request(req, 0,
 						brq->data.bytes_xfered);
-				spin_unlock_irq(&md->lock);
-			}
+			spin_unlock_irq(&md->lock);
 			/*
 			 * If the blk_end_request function returns non-zero even
 			 * though all data has been transferred and no errors
@@ -1831,8 +1348,7 @@
 			err = mmc_blk_reset(md, card->host, type);
 			if (!err)
 				break;
-			if (err == -ENODEV ||
-				mq_rq->packed_cmd != MMC_PACKED_NONE)
+			if (err == -ENODEV)
 				goto cmd_abort;
 			/* Fall through */
 		}
@@ -1861,60 +1377,27 @@
 		}
 
 		if (ret) {
-			if (mq_rq->packed_cmd == MMC_PACKED_NONE) {
-				/*
-				 * In case of a incomplete request
-				 * prepare it again and resend.
-				 */
-				mmc_blk_rw_rq_prep(mq_rq, card,
-						disable_multi, mq);
-				mmc_start_req(card->host,
-						&mq_rq->mmc_active, NULL);
-			} else {
-				if (!mq_rq->packed_retries)
-					goto cmd_abort;
-				mmc_blk_packed_hdr_wrq_prep(mq_rq, card, mq);
-				if (mq_rq->packed_cmd == MMC_PACKED_READ) {
-					mmc_start_req(card->host,
-						&mq->mqrq_hdr->mmc_active,
-						NULL);
-					if (mmc_blk_issue_packed_rd(mq, mq_rq))
-						goto cmd_abort;
-				} else
-					mmc_start_req(card->host,
-						&mq_rq->mmc_active, NULL);
-			}
+			/*
+			 * In case of a incomplete request
+			 * prepare it again and resend.
+			 */
+			mmc_blk_rw_rq_prep(mq_rq, card, disable_multi, mq);
+			mmc_start_req(card->host, &mq_rq->mmc_active, NULL);
 		}
 	} while (ret);
 
-snd_packed_rd:
-	if (mq->mqrq_cur->packed_cmd == MMC_PACKED_READ) {
-		if (mmc_blk_issue_packed_rd(mq, mq->mqrq_cur))
-			goto start_new_req;
-	}
 	return 1;
 
  cmd_abort:
-	if (mq_rq->packed_cmd == MMC_PACKED_NONE) {
-		spin_lock_irq(&md->lock);
-		if (mmc_card_removed(card))
-			req->cmd_flags |= REQ_QUIET;
-		while (ret)
-			ret = __blk_end_request(req, -EIO,
-					blk_rq_cur_bytes(req));
-		spin_unlock_irq(&md->lock);
-	} else {
-		mmc_blk_abort_packed_req(mq, mq_rq);
-	}
+	spin_lock_irq(&md->lock);
+	if (mmc_card_removed(card))
+		req->cmd_flags |= REQ_QUIET;
+	while (ret)
+		ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req));
+	spin_unlock_irq(&md->lock);
 
  start_new_req:
 	if (rqc) {
-		/*
-		 * If current request is packed, it needs to put back.
-		 */
-		if (mq->mqrq_cur->packed_cmd != MMC_PACKED_NONE)
-			mmc_blk_revert_packed_req(mq, mq->mqrq_cur);
-
 		mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
 		mmc_start_req(card->host, &mq->mqrq_cur->mmc_active, NULL);
 	}
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index b3a0896..996f8e3 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -166,7 +166,6 @@
 	int ret;
 	struct mmc_queue_req *mqrq_cur = &mq->mqrq[0];
 	struct mmc_queue_req *mqrq_prev = &mq->mqrq[1];
-	struct mmc_queue_req *mqrq_hdr = &mq->mqrq[2];
 
 	if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask)
 		limit = *mmc_dev(host)->dma_mask;
@@ -178,15 +177,8 @@
 
 	memset(&mq->mqrq_cur, 0, sizeof(mq->mqrq_cur));
 	memset(&mq->mqrq_prev, 0, sizeof(mq->mqrq_prev));
-	memset(&mq->mqrq_hdr, 0, sizeof(mq->mqrq_hdr));
-
-	INIT_LIST_HEAD(&mqrq_cur->packed_list);
-	INIT_LIST_HEAD(&mqrq_prev->packed_list);
-	INIT_LIST_HEAD(&mqrq_hdr->packed_list);
-
 	mq->mqrq_cur = mqrq_cur;
 	mq->mqrq_prev = mqrq_prev;
-	mq->mqrq_hdr = mqrq_hdr;
 	mq->queue->queuedata = mq;
 
 	blk_queue_prep_rq(mq->queue, mmc_prep_request);
@@ -222,21 +214,9 @@
 				kfree(mqrq_cur->bounce_buf);
 				mqrq_cur->bounce_buf = NULL;
 			}
-
-			mqrq_hdr->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
-			if (!mqrq_hdr->bounce_buf) {
-				printk(KERN_WARNING "%s: unable to "
-						"allocate bounce hdr buffer\n",
-						mmc_card_name(card));
-				kfree(mqrq_cur->bounce_buf);
-				mqrq_cur->bounce_buf = NULL;
-				kfree(mqrq_prev->bounce_buf);
-				mqrq_prev->bounce_buf = NULL;
-			}
 		}
 
-		if (mqrq_cur->bounce_buf && mqrq_prev->bounce_buf &&
-				mqrq_hdr->bounce_buf) {
+		if (mqrq_cur->bounce_buf && mqrq_prev->bounce_buf) {
 			blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY);
 			blk_queue_max_hw_sectors(mq->queue, bouncesz / 512);
 			blk_queue_max_segments(mq->queue, bouncesz / 512);
@@ -259,21 +239,11 @@
 				mmc_alloc_sg(bouncesz / 512, &ret);
 			if (ret)
 				goto cleanup_queue;
-
-			mqrq_hdr->sg = mmc_alloc_sg(1, &ret);
-			if (ret)
-				goto cleanup_queue;
-
-			mqrq_hdr->bounce_sg =
-				mmc_alloc_sg(bouncesz / 512, &ret);
-			if (ret)
-				goto cleanup_queue;
 		}
 	}
 #endif
 
-	if (!mqrq_cur->bounce_buf && !mqrq_prev->bounce_buf &&
-			!mqrq_hdr->bounce_buf) {
+	if (!mqrq_cur->bounce_buf && !mqrq_prev->bounce_buf) {
 		blk_queue_bounce_limit(mq->queue, limit);
 		blk_queue_max_hw_sectors(mq->queue,
 			min(host->max_blk_count, host->max_req_size / 512));
@@ -284,11 +254,8 @@
 		if (ret)
 			goto cleanup_queue;
 
-		mqrq_prev->sg = mmc_alloc_sg(host->max_segs, &ret);
-		if (ret)
-			goto cleanup_queue;
 
-		mqrq_hdr->sg = mmc_alloc_sg(host->max_segs, &ret);
+		mqrq_prev->sg = mmc_alloc_sg(host->max_segs, &ret);
 		if (ret)
 			goto cleanup_queue;
 	}
@@ -309,8 +276,6 @@
 	mqrq_cur->bounce_sg = NULL;
 	kfree(mqrq_prev->bounce_sg);
 	mqrq_prev->bounce_sg = NULL;
-	kfree(mqrq_hdr->bounce_sg);
-	mqrq_hdr->bounce_sg = NULL;
 
  cleanup_queue:
 	kfree(mqrq_cur->sg);
@@ -323,11 +288,6 @@
 	kfree(mqrq_prev->bounce_buf);
 	mqrq_prev->bounce_buf = NULL;
 
-	kfree(mqrq_hdr->sg);
-	mqrq_hdr->sg = NULL;
-	kfree(mqrq_hdr->bounce_buf);
-	mqrq_hdr->bounce_buf = NULL;
-
 	blk_cleanup_queue(mq->queue);
 	return ret;
 }
@@ -338,7 +298,6 @@
 	unsigned long flags;
 	struct mmc_queue_req *mqrq_cur = mq->mqrq_cur;
 	struct mmc_queue_req *mqrq_prev = mq->mqrq_prev;
-	struct mmc_queue_req *mqrq_hdr = mq->mqrq_hdr;
 
 	/* Make sure the queue isn't suspended, as that will deadlock */
 	mmc_queue_resume(mq);
@@ -370,15 +329,6 @@
 	kfree(mqrq_prev->bounce_buf);
 	mqrq_prev->bounce_buf = NULL;
 
-	kfree(mqrq_hdr->bounce_sg);
-	mqrq_prev->bounce_sg = NULL;
-
-	kfree(mqrq_hdr->sg);
-	mqrq_prev->sg = NULL;
-
-	kfree(mqrq_hdr->bounce_buf);
-	mqrq_prev->bounce_buf = NULL;
-
 	mq->card = NULL;
 }
 EXPORT_SYMBOL(mmc_cleanup_queue);
@@ -427,37 +377,6 @@
 	}
 }
 
-static unsigned int mmc_queue_packed_map_sg(struct mmc_queue *mq,
-					    struct mmc_queue_req *mqrq,
-					    struct scatterlist *sg)
-{
-	struct scatterlist *__sg;
-	unsigned int sg_len = 0;
-	struct request *req;
-	enum mmc_packed_sg_flag sg_flag = mqrq->packed_sg_flag;
-
-	if (sg_flag == MMC_PACKED_HDR_SG || sg_flag == MMC_PACKED_WR_SG) {
-		__sg = sg;
-		sg_set_buf(__sg, mqrq->packed_cmd_hdr,
-				sizeof(mqrq->packed_cmd_hdr));
-		sg_len++;
-		if (sg_flag == MMC_PACKED_HDR_SG) {
-			sg_mark_end(__sg);
-			return sg_len;
-		}
-		__sg->page_link &= ~0x02;
-	}
-
-	__sg = sg + sg_len;
-	list_for_each_entry(req, &mqrq->packed_list, queuelist) {
-		sg_len += blk_rq_map_sg(mq->queue, req, __sg);
-		__sg = sg + (sg_len - 1);
-		(__sg++)->page_link &= ~0x02;
-	}
-	sg_mark_end(sg + (sg_len - 1));
-	return sg_len;
-}
-
 /*
  * Prepare the sg list(s) to be handed of to the host driver
  */
@@ -466,22 +385,14 @@
 	unsigned int sg_len;
 	size_t buflen;
 	struct scatterlist *sg;
-	enum mmc_packed_sg_flag sg_flag = mqrq->packed_sg_flag;
 	int i;
 
-	if (!mqrq->bounce_buf) {
-		if (sg_flag != MMC_PACKED_NONE_SG)
-			return mmc_queue_packed_map_sg(mq, mqrq, mqrq->sg);
-		else
-			return blk_rq_map_sg(mq->queue, mqrq->req, mqrq->sg);
-	}
+	if (!mqrq->bounce_buf)
+		return blk_rq_map_sg(mq->queue, mqrq->req, mqrq->sg);
 
 	BUG_ON(!mqrq->bounce_sg);
 
-	if (sg_flag != MMC_PACKED_NONE_SG)
-		sg_len = mmc_queue_packed_map_sg(mq, mqrq, mqrq->bounce_sg);
-	else
-		sg_len = blk_rq_map_sg(mq->queue, mqrq->req, mqrq->bounce_sg);
+	sg_len = blk_rq_map_sg(mq->queue, mqrq->req, mqrq->bounce_sg);
 
 	mqrq->bounce_sg_len = sg_len;
 
@@ -503,8 +414,7 @@
 	if (!mqrq->bounce_buf)
 		return;
 
-	if (rq_data_dir(mqrq->req) != WRITE &&
-			mqrq->packed_cmd != MMC_PACKED_WR_HDR)
+	if (rq_data_dir(mqrq->req) != WRITE)
 		return;
 
 	sg_copy_to_buffer(mqrq->bounce_sg, mqrq->bounce_sg_len,
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index bbd8e4c..d2a1eb4 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -12,36 +12,14 @@
 	struct mmc_data		data;
 };
 
-enum mmc_packed_cmd {
-	MMC_PACKED_NONE = 0,
-	MMC_PACKED_WR_HDR,
-	MMC_PACKED_WRITE,
-	MMC_PACKED_READ,
-};
-
-enum mmc_packed_sg_flag {
-	MMC_PACKED_NONE_SG = 0,
-	MMC_PACKED_HDR_SG,
-	MMC_PACKED_WR_SG,
-	MMC_PACKED_RD_SG,
-};
-
 struct mmc_queue_req {
 	struct request		*req;
 	struct mmc_blk_request	brq;
 	struct scatterlist	*sg;
-	enum mmc_packed_sg_flag packed_sg_flag;
 	char			*bounce_buf;
 	struct scatterlist	*bounce_sg;
 	unsigned int		bounce_sg_len;
 	struct mmc_async_req	mmc_active;
-	struct list_head	packed_list;
-	u32			packed_cmd_hdr[128];
-	unsigned int		packed_blocks;
-	enum mmc_packed_cmd	packed_cmd;
-	int		packed_retries;
-	int		packed_fail_idx;
-	u8		packed_num;
 };
 
 struct mmc_queue {
@@ -52,10 +30,9 @@
 	int			(*issue_fn)(struct mmc_queue *, struct request *);
 	void			*data;
 	struct request_queue	*queue;
-	struct mmc_queue_req	mqrq[3];
+	struct mmc_queue_req	mqrq[2];
 	struct mmc_queue_req	*mqrq_cur;
 	struct mmc_queue_req	*mqrq_prev;
-	struct mmc_queue_req	*mqrq_hdr;
 };
 
 extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 9b77227..7b0f4a8 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -348,12 +348,8 @@
 	struct mmc_async_req *data = host->areq;
 
 	/* Prepare a new request */
-	if (areq && !areq->__cond) {
+	if (areq)
 		mmc_pre_req(host, areq->mrq, !host->areq);
-		if (areq->__mrq) {
-			mmc_pre_req(host, areq->__mrq, 0);
-		}
-	}
 
 	if (host->areq) {
 		mmc_wait_for_req_done(host, host->areq->mrq);
@@ -367,11 +363,8 @@
 		mmc_post_req(host, host->areq->mrq, 0);
 
 	 /* Cancel a prepared request if it was not started. */
-	if ((err || start_err) && areq) {
-		mmc_post_req(host, areq->mrq, -EINVAL);
-		if (areq->__mrq)
-			mmc_post_req(host, areq->__mrq, -EINVAL);
-	}
+	if ((err || start_err) && areq)
+			mmc_post_req(host, areq->mrq, -EINVAL);
 
 	if (err)
 		host->areq = NULL;
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index aafc946..2a9b7b1 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -516,11 +516,6 @@
 		} else {
 			card->ext_csd.data_tag_unit_size = 0;
 		}
-
-		card->ext_csd.max_packed_writes =
-			ext_csd[EXT_CSD_MAX_PACKED_WRITES];
-		card->ext_csd.max_packed_reads =
-			ext_csd[EXT_CSD_MAX_PACKED_READS];
 	}
 
 out:
@@ -1257,26 +1252,6 @@
 		}
 	}
 
-	if ((host->caps2 & MMC_CAP2_PACKED_WR &&
-			card->ext_csd.max_packed_writes > 0) ||
-	    (host->caps2 & MMC_CAP2_PACKED_RD &&
-			card->ext_csd.max_packed_reads > 0)) {
-		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-				EXT_CSD_EXP_EVENTS_CTRL,
-				EXT_CSD_PACKED_EVENT_EN,
-				card->ext_csd.generic_cmd6_time);
-		if (err && err != -EBADMSG)
-			goto free_card;
-		if (err) {
-			pr_warning("%s: Enabling packed event failed\n",
-					mmc_hostname(card->host));
-			card->ext_csd.packed_event_en = 0;
-			err = 0;
-		} else {
-			card->ext_csd.packed_event_en = 1;
-		}
-	}
-
 	if (!oldcard)
 		host->card = card;
 
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 2a2fed8..69370f4 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -335,7 +335,6 @@
 	return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD,
 			ext_csd, 512);
 }
-EXPORT_SYMBOL_GPL(mmc_send_ext_csd);
 
 int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
 {
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index 5af95927..f39fafe 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -1,9 +1,17 @@
 #
 # Near Field Communication (NFC) devices
 #
+menuconfig NFC_DEVICES
+	bool "Near Field Communication (NFC) devices"
+	default n
+	---help---
+	  You'll have to say Y if your computer contains an NFC device that
+	  you want to use under Linux.
 
-menu "Near Field Communication (NFC) devices"
-	depends on NFC
+	  You can say N here if you don't have any Near Field Communication
+	  devices connected to your computer.
+
+if NFC_DEVICES
 
 config PN544_NFC
 	tristate "PN544 NFC driver"
@@ -38,4 +46,15 @@
 	  Say Y here to compile support for Texas Instrument's NFC WiLink driver
 	  into the kernel or say M to compile it as module.
 
-endmenu
+config BCM2079X_NFC_SPI
+	tristate "BCM2079X NFC driver for SPI interface"
+	depends on SPI
+	default n
+	help
+	  Say yes if you want BCM2079X Near Field Communication driver.
+	  This is for spi connected version. If unsure, say N here.
+
+	  To compile this driver as a module, choose m here. The module will
+	  be called bcm2079x-spi.
+
+endif # NFC_DEVICES
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
index ab99e85..4aa1a77 100644
--- a/drivers/nfc/Makefile
+++ b/drivers/nfc/Makefile
@@ -5,5 +5,5 @@
 obj-$(CONFIG_PN544_NFC)		+= pn544.o
 obj-$(CONFIG_NFC_PN533)		+= pn533.o
 obj-$(CONFIG_NFC_WILINK)	+= nfcwilink.o
-
+obj-$(CONFIG_BCM2079X_NFC_SPI)	+= bcm2079x-spi.o
 ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/drivers/nfc/bcm2079x-spi.c b/drivers/nfc/bcm2079x-spi.c
new file mode 100755
index 0000000..929b8eb
--- /dev/null
+++ b/drivers/nfc/bcm2079x-spi.c
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2011 Broadcom Corporation.
+ *
+ * Author: Kevin Park <spark@broadcom.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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/spi/spi.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/nfc/bcm2079x.h>
+#include <linux/poll.h>
+#include <linux/version.h>
+
+#ifndef U16_TO_STREAM
+#define U16_TO_STREAM(p, var16)	{*(p)++ = (u8)((var16)>>8); \
+				*(p)++ = (u8)(var16&0xFF); }
+#endif
+#ifndef U8_TO_STREAM
+#define U8_TO_STREAM(p, var8)	{*(p)++ = (u8)(var8); }
+#endif
+
+#ifndef STREAM_TO_U16
+#define STREAM_TO_U16(var16, p)	{(var16) = ((u16)(*((u8 *)p+1)) + \
+				((u16)(*((u8 *)p) << 8))); }
+#endif
+
+#define STATE_HIGH		1
+#define STATE_LOW		0
+
+#define NFC_REQ_ACTIVE_STATE	STATE_LOW
+
+#define MAX_BUFFER_SIZE		274
+
+struct bcm2079x_dev {
+	wait_queue_head_t read_wq;
+	struct mutex read_mutex;
+	struct spi_device *spi;
+	struct miscdevice bcm2079x_device;
+	unsigned int en_gpio;
+	unsigned int irq_gpio;
+	unsigned int wake_gpio;
+	bool irq_enabled;
+	spinlock_t irq_enabled_lock;
+	unsigned char cs_change;
+	unsigned int packet_size;
+	unsigned int spi_delay; // in ns
+};
+
+static int spi_write_cs_hint(struct spi_device *spi, const void *buf,
+				size_t len, char cs_hint)
+{
+	int ret;
+	struct spi_message m;
+	struct spi_transfer t = {
+		.tx_buf = buf,
+		.len = len,
+		.cs_change = cs_hint,
+	};
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	ret = spi_sync(spi, &m);
+	return ret;
+}
+
+static size_t spi_read_espi(struct spi_device *spi, void *buf,
+				  size_t size)
+{
+	int ret;
+	unsigned char req[2] = { 0x02, 0x00 };
+	unsigned char res[2] = { 0x00, };
+	unsigned short rx_len = 0;
+
+	struct spi_transfer t_c = {
+		.tx_buf = req,
+		.len = 2,
+		.cs_change = 1,
+	};
+	struct spi_transfer t_r = {
+		.rx_buf = res,
+		.len = 2,
+		.cs_change = 1,
+	};
+	struct spi_message m;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t_c, &m);
+	spi_message_add_tail(&t_r, &m);
+	ret = spi_sync(spi, &m);
+
+	if (ret)
+		return -EIO;
+
+	STREAM_TO_U16(rx_len, res);
+
+	if (size < rx_len)
+		rx_len = size;
+
+	spi_message_init(&m);
+	t_r.rx_buf = buf;
+	t_r.len = rx_len;
+	t_r.cs_change = 0;
+	spi_message_add_tail(&t_r, &m);
+	ret = spi_sync(spi, &m);
+
+	if (ret)
+		return -EIO;
+	else
+		return rx_len;
+}
+
+static void bcmspinfc_disable_irq(struct bcm2079x_dev *bcm2079x_dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&bcm2079x_dev->irq_enabled_lock, flags);
+
+	if (bcm2079x_dev->irq_enabled) {
+		disable_irq_nosync(bcm2079x_dev->spi->irq);
+		bcm2079x_dev->irq_enabled = false;
+	}
+
+	spin_unlock_irqrestore(&bcm2079x_dev->irq_enabled_lock, flags);
+}
+
+static void bcmspinfc_enable_irq(struct bcm2079x_dev *bcm2079x_dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&bcm2079x_dev->irq_enabled_lock, flags);
+
+	if (!bcm2079x_dev->irq_enabled) {
+		bcm2079x_dev->irq_enabled = true;
+		enable_irq(bcm2079x_dev->spi->irq);
+	}
+
+	spin_unlock_irqrestore(&bcm2079x_dev->irq_enabled_lock, flags);
+}
+
+static irqreturn_t bcm2079x_dev_irq_handler(int irq, void *dev_id)
+{
+	struct bcm2079x_dev *bcm2079x_dev = dev_id;
+
+	if (gpio_get_value(bcm2079x_dev->irq_gpio) != NFC_REQ_ACTIVE_STATE)
+		return IRQ_HANDLED;
+
+	bcmspinfc_disable_irq(bcm2079x_dev);
+
+	/* Wake up waiting readers */
+	wake_up(&bcm2079x_dev->read_wq);
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int bcm2079x_dev_poll(struct file *filp, poll_table *wait)
+{
+	struct bcm2079x_dev *bcm2079x_dev = filp->private_data;
+	unsigned int mask = 0;
+
+	poll_wait(filp, &bcm2079x_dev->read_wq, wait);
+
+	if (gpio_get_value(bcm2079x_dev->irq_gpio) == NFC_REQ_ACTIVE_STATE)
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+
+static ssize_t bcm2079x_dev_read(struct file *filp, char __user *buf,
+				 size_t count, loff_t *offset)
+{
+	struct bcm2079x_dev *bcm2079x_dev = filp->private_data;
+	char p_rcv[MAX_BUFFER_SIZE];
+	int ret;
+	int packets;
+
+	ret = 0;
+	packets = 0;
+
+	if (count > MAX_BUFFER_SIZE)
+		count = MAX_BUFFER_SIZE;
+
+	if (bcm2079x_dev->packet_size > 0)
+		count = bcm2079x_dev->packet_size;
+
+	mutex_lock(&bcm2079x_dev->read_mutex);
+
+	/* Check the IRQ state after mutex delays */
+	if (gpio_get_value(bcm2079x_dev->irq_gpio) == NFC_REQ_ACTIVE_STATE)
+		ret = spi_read_espi(bcm2079x_dev->spi, p_rcv, MAX_BUFFER_SIZE);
+	else
+		ret = 0;
+
+	/* bcm2079x requires 1 SCK delay between transactions */
+	ndelay(bcm2079x_dev->spi_delay);
+
+	mutex_unlock(&bcm2079x_dev->read_mutex);
+
+	if (ret > 0 && copy_to_user(buf, &p_rcv, ret)) {
+		dev_err(&bcm2079x_dev->spi->dev,
+			"failed to copy to user space, rx_cnt = %d\n", ret);
+		ret = -EIO;
+	}
+
+	bcmspinfc_enable_irq(bcm2079x_dev);
+
+	return ret;
+}
+
+static ssize_t bcm2079x_dev_write(struct file *filp, const char __user *buf,
+				  size_t count, loff_t *offset)
+{
+	struct bcm2079x_dev *bcm2079x_dev = filp->private_data;
+	char txbuff[MAX_BUFFER_SIZE];
+	char *p = txbuff;
+	int ret;
+
+	if (count + 4 > MAX_BUFFER_SIZE) {
+		dev_err(&bcm2079x_dev->spi->dev, "out of max memory\n");
+		return -ENOMEM;
+	}
+
+	if (copy_from_user(&txbuff[4], buf, count)) {
+		dev_err(&bcm2079x_dev->spi->dev,
+			"failed to copy from user space\n");
+		return -EFAULT;
+	}
+
+	bcmspinfc_disable_irq(bcm2079x_dev);
+	/* Direct Write */
+	U8_TO_STREAM(p, 0x01);
+	/* Reserved */
+	U8_TO_STREAM(p, 0x00);
+	/* Length */
+	U16_TO_STREAM(p, count);
+
+	mutex_lock(&bcm2079x_dev->read_mutex);
+
+	/* Write data */
+	ret = spi_write_cs_hint(bcm2079x_dev->spi, txbuff, count + 4,
+				bcm2079x_dev->cs_change);
+
+	/* Restore cs_change hit */
+	bcm2079x_dev->cs_change = 0;
+
+	if (ret != 0) {
+		dev_err(&bcm2079x_dev->spi->dev, "write %d\n", ret);
+		ret = -EIO;
+	} else {
+		ret = count;
+	}
+
+	/* bcm2079x requires 1 SCK delay between transactions */
+	ndelay(bcm2079x_dev->spi_delay);
+
+	mutex_unlock(&bcm2079x_dev->read_mutex);
+	bcmspinfc_enable_irq(bcm2079x_dev);
+
+	return ret;
+}
+
+static int bcm2079x_dev_open(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct bcm2079x_dev *bcm2079x_dev = container_of(filp->private_data,
+							 struct bcm2079x_dev,
+							 bcm2079x_device);
+
+	filp->private_data = bcm2079x_dev;
+
+	return ret;
+}
+
+static long bcm2079x_dev_unlocked_ioctl(struct file *filp,
+					unsigned int cmd, unsigned long arg)
+{
+	struct bcm2079x_dev *bcm2079x_dev = filp->private_data;
+	long ret = 0;
+
+	switch (cmd) {
+	case BCMNFC_READ_FULL_PACKET:
+		bcm2079x_dev->packet_size = arg;
+		break;
+	case BCMNFC_POWER_CTL:
+		gpio_set_value(bcm2079x_dev->en_gpio, arg);
+		break;
+	case BCMNFC_WAKE_CTL:
+		if (bcm2079x_dev->wake_gpio < 0) {
+			bcm2079x_dev->cs_change = 1;
+			ret = 1;
+		}
+		gpio_set_value(bcm2079x_dev->wake_gpio, arg);
+		break;
+	default:
+		dev_err(&bcm2079x_dev->spi->dev,
+			"%s, unknown cmd (%x, %lx)\n", __func__, cmd, arg);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static const struct file_operations bcm2079x_dev_fops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.poll = bcm2079x_dev_poll,
+	.read = bcm2079x_dev_read,
+	.write = bcm2079x_dev_write,
+	.open = bcm2079x_dev_open,
+	.unlocked_ioctl = bcm2079x_dev_unlocked_ioctl
+};
+
+static int bcmspinfc_probe(struct spi_device *spi)
+{
+	int ret;
+	struct bcm2079x_platform_data *platform_data;
+	struct bcm2079x_dev *bcm2079x_dev;
+	unsigned int spi_speed_hz = spi->max_speed_hz;
+
+	platform_data = spi->dev.platform_data;
+
+	dev_info(&spi->dev, "%s, probing bcmspinfc driver\n", __func__);
+
+	if (platform_data == NULL) {
+		dev_err(&spi->dev, "nfc probe fail\n");
+		ret = -ENODEV;
+		goto err_nodev;
+	}
+
+	ret = gpio_request(platform_data->irq_gpio, "nfc_spi_int");
+	if (ret) {
+		ret = -ENODEV;
+		goto err_nodev;
+	}
+
+	ret = gpio_request(platform_data->en_gpio, "nfc_en");
+	if (ret)
+		goto err_en;
+
+	/* Wake pin can be joined with SPI_CSN */
+	if (platform_data->wake_gpio > 0) {
+		ret = gpio_request(platform_data->wake_gpio, "nfc_wake");
+		if (ret) {
+			pr_err("%s : nfc_wake request error", __func__);
+			goto err_firm;
+		}
+	}
+
+	gpio_direction_output(platform_data->en_gpio, 0);
+
+	/* Wake pin can be joined with SPI_CSN */
+	if (platform_data->wake_gpio > 0)
+		gpio_direction_output(platform_data->wake_gpio, 0);
+
+	gpio_direction_input(platform_data->irq_gpio);
+	gpio_set_value(platform_data->en_gpio, 0);
+	gpio_set_value(platform_data->wake_gpio, 0);
+
+	bcm2079x_dev = kzalloc(sizeof(*bcm2079x_dev), GFP_KERNEL);
+	if (bcm2079x_dev == NULL) {
+		dev_err(&spi->dev,
+			"failed to allocate memory for module data\n");
+		ret = -ENOMEM;
+		goto err_exit;
+	}
+
+	bcm2079x_dev->wake_gpio = platform_data->wake_gpio;
+	bcm2079x_dev->irq_gpio = platform_data->irq_gpio;
+	bcm2079x_dev->en_gpio = platform_data->en_gpio;
+	bcm2079x_dev->spi = spi;
+	if (spi_speed_hz == 0)
+		bcm2079x_dev->spi_delay = 5000;
+	else
+		bcm2079x_dev->spi_delay = 1000000000 / spi_speed_hz;
+
+	/* init mutex and queues */
+	init_waitqueue_head(&bcm2079x_dev->read_wq);
+	mutex_init(&bcm2079x_dev->read_mutex);
+	spin_lock_init(&bcm2079x_dev->irq_enabled_lock);
+
+	bcm2079x_dev->bcm2079x_device.minor = MISC_DYNAMIC_MINOR;
+	bcm2079x_dev->bcm2079x_device.name = "bcm2079x";
+	bcm2079x_dev->bcm2079x_device.fops = &bcm2079x_dev_fops;
+
+	ret = misc_register(&bcm2079x_dev->bcm2079x_device);
+	if (ret) {
+		dev_err(&spi->dev, "misc_register failed\n");
+		goto err_misc_register;
+	}
+
+	/* request irq.  the irq is set whenever the chip has data available
+	 * for reading.  it is cleared when all data has been read.
+	 */
+	dev_info(&spi->dev, "requesting IRQ %d\n", spi->irq);
+	bcm2079x_dev->irq_enabled = true;
+	ret = request_irq(spi->irq, bcm2079x_dev_irq_handler,
+			  IRQF_TRIGGER_FALLING, spi->modalias,
+			  bcm2079x_dev);
+	if (ret) {
+		dev_err(&spi->dev, "request_irq failed\n");
+		goto err_request_irq_failed;
+	}
+	bcmspinfc_disable_irq(bcm2079x_dev);
+	spi_set_drvdata(spi, bcm2079x_dev);
+
+	bcm2079x_dev->packet_size = 0;
+	dev_info(&spi->dev,
+		 "%s, probing bcmspinfc driver exited successfully\n",
+		 __func__);
+
+	return 0;
+
+err_request_irq_failed:
+	misc_deregister(&bcm2079x_dev->bcm2079x_device);
+err_misc_register:
+	mutex_destroy(&bcm2079x_dev->read_mutex);
+	kfree(bcm2079x_dev);
+err_exit:
+	gpio_free(platform_data->wake_gpio);
+err_firm:
+	gpio_free(platform_data->en_gpio);
+err_en:
+	gpio_free(platform_data->irq_gpio);
+err_nodev:
+	return ret;
+}
+
+static int bcmspinfc_remove(struct spi_device *spi)
+{
+	struct bcm2079x_dev *bcm2079x_dev;
+
+	bcm2079x_dev = spi_get_drvdata(spi);
+
+	free_irq(spi->irq, bcm2079x_dev);
+	misc_deregister(&bcm2079x_dev->bcm2079x_device);
+	mutex_destroy(&bcm2079x_dev->read_mutex);
+	gpio_free(bcm2079x_dev->wake_gpio);
+	gpio_free(bcm2079x_dev->en_gpio);
+	gpio_free(bcm2079x_dev->irq_gpio);
+	kfree(bcm2079x_dev);
+
+	return 0;
+}
+
+static struct spi_driver bcmspinfc_driver = {
+	.driver = {
+		   .owner = THIS_MODULE,
+		   .name = "bcm2079x-spi",
+		   },
+	.probe = bcmspinfc_probe,
+	.remove = bcmspinfc_remove,
+};
+
+/*
+ * module load/unload record keeping
+ */
+
+static int __init bcm2079x_dev_init(void)
+{
+	return spi_register_driver(&bcmspinfc_driver);
+}
+module_init(bcm2079x_dev_init);
+
+static void __exit bcm2079x_dev_exit(void)
+{
+	spi_unregister_driver(&bcmspinfc_driver);
+}
+module_exit(bcm2079x_dev_exit);
+
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("NFC bcmspinfc driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 50adc1b..5b3bbbf 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -97,6 +97,17 @@
 	  Say Y here to enable support for the DS2782/DS2786 standalone battery
 	  gas-gauge.
 
+config FUELGAUGE_DS2784
+	tristate "MAXIM DS2784 Fuel Gauge"
+	select W1
+	select W1_SLAVE_DS2784
+	help
+	  Say Y to enable support for the DS2784 fuel gauge chip.
+
+	  This fuel gauge chip is used to monitor remaining battery
+	  capacity, and possibly protect against charging errors, for
+	  Li+ batteries.
+
 config BATTERY_PMU
 	tristate "Apple PMU battery"
 	depends on PPC32 && ADB_PMU
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index c8b0352..147e6c1 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -45,3 +45,4 @@
 obj-$(CONFIG_CHARGER_MAX8998)	+= max8998_charger.o
 obj-$(CONFIG_CHARGER_SMB347)	+= smb347-charger.o
 obj-$(CONFIG_BATTERY_ANDROID)	+= android_battery.o
+obj-$(CONFIG_FUELGAUGE_DS2784)  += ds2784_fuelgauge.o
diff --git a/drivers/power/ds2784_fuelgauge.c b/drivers/power/ds2784_fuelgauge.c
new file mode 100644
index 0000000..bec4297
--- /dev/null
+++ b/drivers/power/ds2784_fuelgauge.c
@@ -0,0 +1,307 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+* Copyright (C) 2012 Samsung Electronics Co., Ltd.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/spinlock.h>
+#include <linux/power_supply.h>
+#include <linux/debugfs.h>
+
+#include "../w1/w1.h"
+#include "../w1/slaves/w1_ds2784.h"
+
+struct fuelgauge_status {
+	int timestamp;
+
+	int voltage_uV;		/* units of uV */
+	int current_uA;		/* units of uA */
+	int charge_uAh;
+
+	short temp_C;		/* units of 0.1 C */
+
+	u8 percentage;		/* battery percentage */
+	u8 charge_source;
+	u8 status_reg;
+	u8 battery_full;	/* battery full (don't charge) */
+
+	u8 cooldown;		/* was overtemp */
+	u8 charge_mode;
+};
+
+struct ds2784_info {
+	struct device			*dev;
+	struct device			*w1_dev;
+	struct power_supply		bat;
+	struct delayed_work		work;
+	char				raw[DS2784_DATA_SIZE];
+	struct fuelgauge_status		status;
+	bool				inited;
+	struct dentry			*dentry;
+};
+
+static int ds2784_read(struct ds2784_info *di, char *buf, int addr,
+		       size_t count)
+{
+	return w1_ds2784_read(di->w1_dev, buf, addr, count);
+}
+
+static int ds2784_get_soc(struct ds2784_info *di, int *soc)
+{
+	int ret;
+
+	ret = ds2784_read(di, di->raw + DS2784_REG_RARC, DS2784_REG_RARC, 1);
+
+	if (ret < 0)
+		return ret;
+
+	di->status.percentage =	di->raw[DS2784_REG_RARC];
+	pr_debug("%s: level : %d\n", __func__, di->status.percentage);
+	*soc = di->status.percentage;
+	return 0;
+}
+
+static int ds2784_get_vcell(struct ds2784_info *di, int *vcell)
+{
+	short n;
+	int ret;
+
+	ret = ds2784_read(di, di->raw + DS2784_REG_VOLT_MSB,
+			  DS2784_REG_VOLT_MSB, 2);
+
+	if (ret < 0)
+		return ret;
+
+	n = (((di->raw[DS2784_REG_VOLT_MSB] << 8) |
+	      (di->raw[DS2784_REG_VOLT_LSB])) >> 5);
+	di->status.voltage_uV = n * 4886;
+	pr_debug("%s: voltage : %d\n", __func__, di->status.voltage_uV);
+	*vcell = di->status.voltage_uV;
+	return 0;
+}
+
+static int ds2784_get_current(struct ds2784_info *di, bool avg, int *ival)
+{
+	int reg = avg ? DS2784_REG_AVG_CURR_MSB : DS2784_REG_CURR_MSB;
+	short n;
+	int ret;
+
+	if (!di->raw[DS2784_REG_RSNSP]) {
+		ret = ds2784_read(di, di->raw + DS2784_REG_RSNSP,
+				  DS2784_REG_RSNSP, 1);
+		if (ret < 0)
+			dev_err(di->dev, "error %d reading RSNSP\n", ret);
+	}
+
+	ret = ds2784_read(di, di->raw + reg, reg, 2);
+	if (ret < 0)
+		return ret;
+
+	n = ((di->raw[reg] << 8) | (di->raw[reg+1]));
+
+	*ival = (n * 15625) / 10000 * di->raw[DS2784_REG_RSNSP] / 1000;
+	return 0;
+}
+
+static int ds2784_get_current_now(struct ds2784_info *di, int *i_current)
+{
+	return ds2784_get_current(di, false, i_current);
+}
+
+static int ds2784_get_current_avg(struct ds2784_info *di, int *i_avg)
+{
+	return ds2784_get_current(di, true, i_avg);
+}
+
+static int ds2784_get_temperature(struct ds2784_info *di, int *temp_now)
+{
+	short n;
+	int ret;
+
+	ret = ds2784_read(di, di->raw + DS2784_REG_TEMP_MSB,
+			  DS2784_REG_TEMP_MSB, 2);
+
+	if (ret < 0)
+		return ret;
+
+	n = (((di->raw[DS2784_REG_TEMP_MSB] << 8) |
+			(di->raw[DS2784_REG_TEMP_LSB])) >> 5);
+
+	if (di->raw[DS2784_REG_TEMP_MSB] & (1 << 7))
+		n |= 0xf800;
+
+	di->status.temp_C = (n * 10) / 8;
+	pr_debug("%s: temp : %d\n", __func__, di->status.temp_C);
+
+	*temp_now = di->status.temp_C;
+	return 0;
+}
+
+static int ds2784_get_property(struct power_supply *psy,
+	enum power_supply_property psp,
+	union power_supply_propval *val)
+{
+	int ret = 0;
+	struct ds2784_info *di = container_of(psy, struct ds2784_info, bat);
+
+	if (!di->inited)
+		return -ENODEV;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		ret = ds2784_get_vcell(di, &val->intval);
+		break;
+
+	case POWER_SUPPLY_PROP_TEMP:
+		ret = ds2784_get_temperature(di, &val->intval);
+		break;
+
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		val->strval = "DS2784";
+		break;
+
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		val->strval = "Maxim/Dallas";
+		break;
+
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		ret = ds2784_get_current_now(di, &val->intval);
+		break;
+
+	case POWER_SUPPLY_PROP_CURRENT_AVG:
+		ret = ds2784_get_current_avg(di, &val->intval);
+		break;
+
+	case POWER_SUPPLY_PROP_CAPACITY:
+		ret = ds2784_get_soc(di, &val->intval);
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static enum power_supply_property ds2784_props[] = {
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CURRENT_AVG,
+	POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static int ds2784_debugfs_show(struct seq_file *s, void *unused)
+{
+	struct ds2784_info *di = s->private;
+	u8 reg;
+
+	ds2784_read(di, di->raw, 0x00, 0x1C);
+	ds2784_read(di, di->raw + 0x20, 0x20, 0x10);
+	ds2784_read(di, di->raw + 0x60, 0x60, 0x20);
+	ds2784_read(di, di->raw + 0xb0, 0xb0, 0x02);
+
+	for (reg = 0x0; reg <= 0xb1; reg++) {
+		if ((reg >= 0x1c && reg <= 0x1f) ||
+			(reg >= 0x38 && reg <= 0x5f) ||
+			(reg >= 0x90 && reg <= 0xaf))
+				continue;
+
+		if (!(reg & 0x7))
+			seq_printf(s, "\n0x%02x:", reg);
+
+		seq_printf(s, "\t0x%02x", di->raw[reg]);
+	}
+	seq_printf(s, "\n");
+	return 0;
+}
+
+static int ds2784_debugfs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ds2784_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations ds2784_debugfs_fops = {
+	.open = ds2784_debugfs_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int __devinit ds2784_probe(struct platform_device *pdev)
+{
+	struct ds2784_info *di;
+	int ret;
+
+	di = kzalloc(sizeof(*di), GFP_KERNEL);
+	if (!di) {
+		pr_err("%s:failed to allocate memory for module data\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, di);
+	di->dev			= &pdev->dev;
+	di->w1_dev		= pdev->dev.parent;
+	di->bat.name		= dev_name(&pdev->dev);
+	di->bat.type		= POWER_SUPPLY_TYPE_BATTERY;
+	di->bat.properties	= ds2784_props;
+	di->bat.num_properties	= ARRAY_SIZE(ds2784_props);
+	di->bat.get_property	= ds2784_get_property;
+
+	ret = power_supply_register(&pdev->dev, &di->bat);
+	if (ret) {
+		dev_err(di->dev, "failed to register battery power supply\n");
+		kfree(di);
+		return ret;
+	}
+
+	di->dentry = debugfs_create_file("ds2784", S_IRUGO, NULL, di,
+					 &ds2784_debugfs_fops);
+	di->inited = true;
+	return 0;
+}
+
+static int __devexit ds2784_remove(struct platform_device *pdev)
+{
+	struct ds2784_info *di = platform_get_drvdata(pdev);
+
+	power_supply_unregister(&di->bat);
+	debugfs_remove(di->dentry);
+	kfree(di);
+	return 0;
+}
+
+static struct platform_driver ds2784_driver = {
+	.probe = ds2784_probe,
+	.remove   = __devexit_p(ds2784_remove),
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "ds2784-fuelgauge",
+	},
+};
+
+module_platform_driver(ds2784_driver);
+
+MODULE_AUTHOR("Samsung");
+MODULE_DESCRIPTION("ds2784 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index b20acfa..dade6bb 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -97,7 +97,8 @@
 		return sprintf(buf, "%s\n", technology_text[value.intval]);
 	else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
 		return sprintf(buf, "%s\n", capacity_level_text[value.intval]);
-	else if (off == POWER_SUPPLY_PROP_TYPE)
+	else if (off == POWER_SUPPLY_PROP_TYPE ||
+		 off == POWER_SUPPLY_PROP_REMOTE_TYPE)
 		return sprintf(buf, "%s\n", type_text[value.intval]);
 	else if (off == POWER_SUPPLY_PROP_SCOPE)
 		return sprintf(buf, "%s\n", scope_text[value.intval]);
@@ -178,6 +179,10 @@
 	POWER_SUPPLY_ATTR(usb_hc),
 	POWER_SUPPLY_ATTR(usb_otg),
 	POWER_SUPPLY_ATTR(charge_enabled),
+	POWER_SUPPLY_ATTR(usb_inpriority),
+	POWER_SUPPLY_ATTR(auto_current_limit),
+	POWER_SUPPLY_ATTR(remote_type),
+	POWER_SUPPLY_ATTR(charger_detection),
 	/* Properties of type `const char *' */
 	POWER_SUPPLY_ATTR(model_name),
 	POWER_SUPPLY_ATTR(manufacturer),
diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c
index fb56ec7..7f0fd6f 100644
--- a/drivers/power/smb347-charger.c
+++ b/drivers/power/smb347-charger.c
@@ -41,11 +41,13 @@
 #define CFG_CURRENT_LIMIT_USB_MASK		0x0f
 #define CFG_VARIOUS_FUNCTION                    0x02
 #define CFG_INPUT_SOURCE_PRIORITY               BIT(2)
+#define CFG_AUTOMATIC_INPUT_CURRENT_LIMIT	BIT(4)
 #define CFG_FLOAT_VOLTAGE			0x03
 #define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK	0xc0
 #define CFG_FLOAT_VOLTAGE_MASK			0x3F
 #define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT	6
 #define CFG_CHARGE_CONTROL			0x04
+#define CFG_AUTOMATIC_POWER_SOURCE_DETECTION	BIT(2)
 #define CFG_AUTOMATIC_RECHARGE_DISABLE		BIT(7)
 #define CFG_STAT				0x05
 #define CFG_STAT_DISABLED			BIT(5)
@@ -123,8 +125,27 @@
 #define STAT_C_CHG_MASK				0x06
 #define STAT_C_CHG_SHIFT			1
 #define STAT_C_CHARGER_ERROR			BIT(6)
+#define STAT_D					0x3e
+#define STAT_D_APSD_RESULT_MASK			0x7
+#define STAT_D_APSD_COMPLETE			BIT(3)
 #define STAT_E					0x3f
 
+/* APSD results */
+#define APSD_RESULT_NOT_RUN			0x0
+#define APSD_RESULT_CDP				0x1
+#define APSD_RESULT_DCP				0x2
+#define APSD_RESULT_OTHER_CHARGER		0x3
+#define APSD_RESULT_SDP				0x4
+#define APSD_RESULT_ACA				0x5
+
+static enum power_supply_type apsd_to_pst[STAT_D_APSD_RESULT_MASK] = {
+	[APSD_RESULT_CDP] = POWER_SUPPLY_TYPE_USB_CDP,
+	[APSD_RESULT_DCP] = POWER_SUPPLY_TYPE_USB_DCP,
+	[APSD_RESULT_SDP] = POWER_SUPPLY_TYPE_USB,
+	[APSD_RESULT_ACA] = POWER_SUPPLY_TYPE_USB_ACA,
+	[APSD_RESULT_OTHER_CHARGER] = POWER_SUPPLY_TYPE_MAINS,
+};
+
 /**
  * struct smb347_charger - smb347 charger instance
  * @lock: protects concurrent access to online variables
@@ -146,6 +167,8 @@
 	struct power_supply	battery;
 	bool			mains_online;
 	bool			usb_online;
+	enum power_supply_type	usb_apsd_result;
+	bool			usb_apsd_enabled;
 	bool			charging_enabled;
 	unsigned int		mains_current_limit;
 	bool			usb_hc_mode;
@@ -231,9 +254,18 @@
 
 static int smb347_read(struct smb347_charger *smb, u8 reg)
 {
+	int t;
 	int ret;
 
-	ret = i2c_smbus_read_byte_data(smb->client, reg);
+	for (t = 0; t < 20; t++) {
+		ret = i2c_smbus_read_byte_data(smb->client, reg);
+		if (ret >= 0)
+			break;
+		dev_dbg(&smb->client->dev, "%s: retry %d reg=0x%x ret=%d\n",
+			__func__, t, reg, ret);
+		msleep(20);
+	}
+
 	if (ret < 0)
 		dev_warn(&smb->client->dev, "failed to read reg 0x%x: %d\n",
 			 reg, ret);
@@ -242,15 +274,46 @@
 
 static int smb347_write(struct smb347_charger *smb, u8 reg, u8 val)
 {
+	int t;
 	int ret;
 
-	ret = i2c_smbus_write_byte_data(smb->client, reg, val);
+	for (t = 0; t < 20; t++) {
+		ret = i2c_smbus_write_byte_data(smb->client, reg, val);
+		if (ret >= 0)
+			break;
+		dev_dbg(&smb->client->dev, "%s: retry %d reg=0x%x ret=%d\n",
+			__func__, t, reg, ret);
+		msleep(20);
+	}
+
 	if (ret < 0)
 		dev_warn(&smb->client->dev, "failed to write reg 0x%x: %d\n",
 			 reg, ret);
 	return ret;
 }
 
+static int smb347_read_block_data(struct smb347_charger *smb, u8 reg,
+				  u8 length, u8 *values)
+{
+	int t;
+	int ret;
+
+	for (t = 0; t < 20; t++) {
+		ret = i2c_smbus_read_i2c_block_data(smb->client, reg, length,
+						    values);
+		if (ret >= 0)
+			break;
+		dev_dbg(&smb->client->dev, "%s: retry %d reg=0x%x ret=%d\n",
+			__func__, t, reg, ret);
+		msleep(20);
+	}
+
+	if (ret < 0)
+		dev_warn(&smb->client->dev,
+			 "failed to block read reg 0x%x: %d\n", reg, ret);
+	return ret;
+}
+
 /**
  * smb347_update_status - updates the charging status
  * @smb: pointer to smb347 charger instance
@@ -759,7 +822,7 @@
 	u8 irqstat[6];
 	irqreturn_t ret = IRQ_NONE;
 
-	t = i2c_smbus_read_i2c_block_data(smb->client, IRQSTAT_A, 6, irqstat);
+	t = smb347_read_block_data(smb, IRQSTAT_A, 6, irqstat);
 	if (t < 0) {
 		dev_warn(&smb->client->dev,
 			 "reading IRQSTAT registers failed\n");
@@ -1018,31 +1081,12 @@
 {
 	struct smb347_charger *smb =
 		container_of(psy, struct smb347_charger, mains);
-	int ret;
 	bool oldval;
 
 	switch (prop) {
 	case POWER_SUPPLY_PROP_ONLINE:
 		oldval = smb->mains_online;
-
 		smb->mains_online = val->intval;
-
-		smb347_set_writable(smb, true);
-
-		ret = smb347_read(smb, CMD_A);
-		if (ret < 0)
-			return -EINVAL;
-
-		ret &= ~CMD_A_SUSPEND_ENABLED;
-		if (val->intval)
-			ret |= CMD_A_SUSPEND_ENABLED;
-
-		ret = smb347_write(smb, CMD_A, ret);
-
-		smb347_hw_init(smb);
-
-		smb347_set_writable(smb, false);
-
 		if (smb->mains_online != oldval)
 			power_supply_changed(psy);
 		return 0;
@@ -1076,6 +1120,108 @@
 	POWER_SUPPLY_PROP_CURRENT_MAX,
 };
 
+static int apsd_detect(struct smb347_charger *smb, bool wait)
+{
+	int i;
+	int tries;
+	int ret;
+
+	smb347_update_status(smb);
+	mutex_lock(&smb->lock);
+
+	/*
+	 * If USBIN input disconnected then use UNKNOWN.
+	 */
+
+	if (!smb->usb_online) {
+		smb->usb_apsd_result = POWER_SUPPLY_TYPE_UNKNOWN;
+		ret = 0;
+		goto unlock;
+	}
+
+	/*
+	 * If APSD is now disabled then return cached result from previous
+	 * run.
+	 */
+
+	if (!smb->usb_apsd_enabled) {
+		ret = 0;
+		goto unlock;
+	}
+
+	tries = wait ? 8 : 1;
+
+	for (i = 1; i <= tries; i++) {
+		ret = smb347_read(smb, STAT_D);
+		if (ret < 0)
+			goto unlock;
+		if (ret & STAT_D_APSD_COMPLETE)
+			goto apsd_done;
+
+		if (i < tries)
+			msleep(100);
+	}
+
+	ret = 0;
+	goto unlock;
+
+apsd_done:
+	smb->usb_apsd_result = apsd_to_pst[ret & STAT_D_APSD_RESULT_MASK];
+	smb->usb.type = smb->usb_apsd_result;
+	ret = 0;
+
+unlock:
+	mutex_unlock(&smb->lock);
+	return ret;
+}
+
+static int __apsd_enable(struct smb347_charger *smb, bool enable)
+{
+	int ret = 0;
+	int chgc;
+
+	smb347_set_writable(smb, true);
+	ret = smb347_read(smb, CFG_CHARGE_CONTROL);
+	if (ret < 0)
+		goto writeoff;
+
+	chgc = ret;
+	if (enable)
+		chgc |= CFG_AUTOMATIC_POWER_SOURCE_DETECTION;
+	else
+		chgc &= ~CFG_AUTOMATIC_POWER_SOURCE_DETECTION;
+
+	ret = smb347_write(smb, CFG_CHARGE_CONTROL, chgc);
+	if (ret)
+		goto writeoff;
+	smb->usb_apsd_enabled = enable;
+	ret = 0;
+
+writeoff:
+	smb347_set_writable(smb, false);
+	return ret;
+}
+
+static int apsd_enable(struct smb347_charger *smb, bool enable)
+{
+	int ret = 0;
+
+	mutex_lock(&smb->lock);
+
+	if (smb->usb_apsd_enabled == enable)
+		goto skip;
+
+	ret = __apsd_enable(smb, enable);
+	mutex_unlock(&smb->lock);
+	if (!ret)
+		ret = apsd_detect(smb, true);
+	return ret;
+
+skip:
+	mutex_unlock(&smb->lock);
+	return ret;
+}
+
 static int smb347_usb_get_property(struct power_supply *psy,
 				   enum power_supply_property prop,
 				   union power_supply_propval *val)
@@ -1096,6 +1242,15 @@
 		val->intval = smb->usb_otg_enabled;
 		return 0;
 
+	case POWER_SUPPLY_PROP_REMOTE_TYPE:
+		apsd_detect(smb, false);
+		val->intval = smb->usb_apsd_result;
+		return 0;
+
+	case POWER_SUPPLY_PROP_CHARGER_DETECTION:
+		val->intval = smb->usb_apsd_enabled;
+		return 0;
+
 	default:
 		break;
 	}
@@ -1115,6 +1270,7 @@
 	case POWER_SUPPLY_PROP_ONLINE:
 		oldval = smb->usb_online;
 		smb->usb_online = val->intval;
+		apsd_detect(smb, true);
 
 		if (smb->usb_online != oldval)
 			power_supply_changed(psy);
@@ -1146,6 +1302,10 @@
 
 		break;
 
+	case POWER_SUPPLY_PROP_CHARGER_DETECTION:
+		ret = apsd_enable(smb, val->intval);
+		break;
+
 	default:
 		break;
 	}
@@ -1159,6 +1319,7 @@
 	switch (prop) {
 	case POWER_SUPPLY_PROP_USB_HC:
 	case POWER_SUPPLY_PROP_USB_OTG:
+	case POWER_SUPPLY_PROP_CHARGER_DETECTION:
 		return 1;
 	default:
 		break;
@@ -1171,6 +1332,8 @@
 	POWER_SUPPLY_PROP_ONLINE,
 	POWER_SUPPLY_PROP_USB_HC,
 	POWER_SUPPLY_PROP_USB_OTG,
+	POWER_SUPPLY_PROP_REMOTE_TYPE,
+	POWER_SUPPLY_PROP_CHARGER_DETECTION,
 };
 
 static int smb347_battery_get_property(struct power_supply *psy,
@@ -1290,6 +1453,20 @@
 		val->strval = pdata->battery_info.name;
 		break;
 
+	case POWER_SUPPLY_PROP_USB_INPRIORITY:
+		ret = smb347_read(smb, CFG_VARIOUS_FUNCTION);
+		if (ret < 0)
+			return ret;
+		val->intval = ret & CFG_INPUT_SOURCE_PRIORITY ? 1 : 0;
+		break;
+
+	case POWER_SUPPLY_PROP_AUTO_CURRENT_LIMIT:
+		ret = smb347_read(smb, CFG_VARIOUS_FUNCTION);
+		if (ret < 0)
+			return ret;
+		val->intval = ret & CFG_AUTOMATIC_INPUT_CURRENT_LIMIT ? 1 : 0;
+		break;
+
 	default:
 		return -EINVAL;
 	}
@@ -1310,6 +1487,50 @@
 		ret = smb347_charging_set(smb, val->intval);
 		break;
 
+	case POWER_SUPPLY_PROP_USB_INPRIORITY:
+		smb347_set_writable(smb, true);
+
+		ret = smb347_read(smb, CMD_A);
+		if (ret < 0)
+			goto priority_fail;
+		ret |= CMD_A_SUSPEND_ENABLED;
+		if (val->intval)
+			ret &= ~CMD_A_SUSPEND_ENABLED;
+		ret = smb347_write(smb, CMD_A, ret);
+		if (ret < 0)
+			goto priority_fail;
+
+		ret = smb347_read(smb, CFG_VARIOUS_FUNCTION);
+		if (ret < 0)
+			goto priority_fail;
+		ret &= ~(CFG_INPUT_SOURCE_PRIORITY);
+		if (val->intval)
+			ret |= CFG_INPUT_SOURCE_PRIORITY;
+		ret = smb347_write(smb, CFG_VARIOUS_FUNCTION, ret);
+		smb347_hw_init(smb);
+		if (ret < 0)
+			goto priority_fail;
+		ret = 0;
+priority_fail:
+		smb347_set_writable(smb, false);
+		break;
+
+	case POWER_SUPPLY_PROP_AUTO_CURRENT_LIMIT:
+		smb347_set_writable(smb, true);
+		ret = smb347_read(smb, CFG_VARIOUS_FUNCTION);
+		if (ret < 0)
+			goto aicl_fail;
+		ret &= ~(CFG_AUTOMATIC_INPUT_CURRENT_LIMIT);
+		if (val->intval)
+			ret |= CFG_AUTOMATIC_INPUT_CURRENT_LIMIT;
+		ret = smb347_write(smb, CFG_VARIOUS_FUNCTION, ret);
+		if (ret < 0)
+			goto aicl_fail;
+		ret = 0;
+aicl_fail:
+		smb347_set_writable(smb, false);
+		break;
+
 	default:
 		break;
 	}
@@ -1322,6 +1543,8 @@
 {
 	switch (prop) {
 	case POWER_SUPPLY_PROP_CHARGE_ENABLED:
+	case POWER_SUPPLY_PROP_USB_INPRIORITY:
+	case POWER_SUPPLY_PROP_AUTO_CURRENT_LIMIT:
 		return 1;
 	default:
 		break;
@@ -1341,6 +1564,8 @@
 	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
 	POWER_SUPPLY_PROP_CHARGE_ENABLED,
 	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_USB_INPRIORITY,
+	POWER_SUPPLY_PROP_AUTO_CURRENT_LIMIT,
 };
 
 static int smb347_debugfs_show(struct seq_file *s, void *data)
@@ -1454,6 +1679,9 @@
 	if (ret < 0)
 		return ret;
 
+	/* Initialize APSD disabled */
+	__apsd_enable(smb, false);
+
 	smb->mains.name = "smb347-mains";
 	smb->mains.type = POWER_SUPPLY_TYPE_MAINS;
 	smb->mains.get_property = smb347_mains_get_property;
@@ -1475,7 +1703,7 @@
 	smb->usb.num_supplicants = ARRAY_SIZE(battery);
 
 	smb->battery.name = "smb347-battery";
-	smb->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+	smb->battery.type = POWER_SUPPLY_TYPE_UNKNOWN;
 	smb->battery.get_property = smb347_battery_get_property;
 	smb->battery.set_property = smb347_battery_set_property;
 	smb->battery.property_is_writeable = smb347_battery_property_is_writeable;
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 36db5a4..12df856 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -149,6 +149,15 @@
 	  regulator via I2C bus. The provided regulator is suitable
 	  for PXA27x chips to control VCC_CORE and VCC_USIM voltages.
 
+config REGULATOR_MAX77686
+	tristate "Maxim 77686 regulator"
+	depends on MFD_MAX77686
+	help
+	  This driver controls a Maxim 77686 regulator via I2C bus or 3 GPIO ports
+	  to control BUCK2, BUCK3, BUCK4.
+	  The provided regulator is suitable for Exynos-4 and Exynos-5
+	  chips to control VARM, VINT voltages and the other LDOs.
+
 config REGULATOR_MAX8649
 	tristate "Maxim 8649 voltage regulator"
 	depends on I2C
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 94b5274..f0ac072 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -24,6 +24,7 @@
 obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
 obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o
 obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
+obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o
 obj-$(CONFIG_REGULATOR_MAX8649)	+= max8649.o
 obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
 obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o
diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c
new file mode 100644
index 0000000..815db80
--- /dev/null
+++ b/drivers/regulator/max77686.c
@@ -0,0 +1,915 @@
+/*
+ * max77686.c - Regulator driver for the Maxim 77686
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Chiwoong Byun <woong.byun@smasung.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
+ *
+ * This driver is based on max8997.c
+ */
+
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/slab.h>
+
+#define MAX77686_OPMODE_SHIFT 6
+#define MAX77686_OPMODE_BUCK234_SHIFT 4
+#define MAX77686_OPMODE_MASK 0x3
+
+#define MAX77686_DVS_VOL_COMP 50000
+
+#define MAX77686_CHIPREV_MASK		0x7
+#define MAX77686_VERSION_MASK		0x78
+
+enum MAX77686_CHIPREV {
+	CHIPREV_MAX77686_PASS1 = 0x1,
+	CHIPREV_MAX77686_PASS2 = 0x2,
+};
+
+enum MAX77686_VERSION {
+	VERSION_MAX77686,
+	VERSION_MAX77686A,
+};
+
+struct max77686_data {
+	struct device *dev;
+	struct max77686_dev *iodev;
+	int num_regulators;
+	struct regulator_dev **rdev;
+	int ramp_delay; /* in mV/us */
+	u8 version;
+	u8 chip_rev;
+
+	struct max77686_opmode_data *opmode_data;
+
+	bool buck2_gpiodvs;
+	bool buck3_gpiodvs;
+	bool buck4_gpiodvs;
+	u8 buck2_vol[8];
+	u8 buck3_vol[8];
+	u8 buck4_vol[8];
+	int buck234_gpios_dvs[3];
+	char *buck234_gpios_dvs_label[3];
+	int buck234_gpios_selb[3];
+	char *buck234_gpios_selb_label[3];
+	int buck234_gpioindex;
+	bool ignore_gpiodvs_side_effect;
+
+	u8 saved_states[MAX77686_REG_MAX];
+};
+
+static inline void max77686_set_gpio(struct max77686_data *max77686)
+{
+	int set3 = (max77686->buck234_gpioindex) & 0x1;
+	int set2 = ((max77686->buck234_gpioindex) >> 1) & 0x1;
+	int set1 = ((max77686->buck234_gpioindex) >> 2) & 0x1;
+
+	if (max77686->buck234_gpios_dvs[0])
+		gpio_set_value(max77686->buck234_gpios_dvs[0], set1);
+	if (max77686->buck234_gpios_dvs[1])
+		gpio_set_value(max77686->buck234_gpios_dvs[1], set2);
+	if (max77686->buck234_gpios_dvs[2])
+		gpio_set_value(max77686->buck234_gpios_dvs[2], set3);
+}
+
+struct voltage_map_desc {
+	int min;
+	int max;
+	int step;
+	unsigned int n_bits;
+};
+
+/* LDO3 ~ 5, 9 ~ 14, 16 ~ 26 (uV) */
+static struct voltage_map_desc ldo_voltage_map_desc = {
+	.min = 800000,	.max = 3950000,	.step = 50000,	.n_bits = 6,
+};
+
+/* LDO1 ~ 2, 6 ~ 8, 15 (uV) */
+static struct voltage_map_desc ldo_low_voltage_map_desc = {
+	.min = 800000,	.max = 2375000,	.step = 25000,	.n_bits = 6,
+};
+
+/* Buck2, 3, 4 (uV) */
+static struct voltage_map_desc buck_dvs_voltage_map_desc = {
+	.min = 600000,	.max = 3787500,	.step = 12500,	.n_bits = 8,
+};
+
+/* Buck1, 5 ~ 9 (uV) */
+static struct voltage_map_desc buck_voltage_map_desc = {
+	.min = 750000,	.max = 3900000,	.step = 50000,	.n_bits = 6,
+};
+
+/* Buck1 (uV) for MAX77686A */
+static struct voltage_map_desc buck1_voltage_map_desc = {
+	.min = 750000,	.max = 1537500,	.step = 12500,	.n_bits = 6,
+};
+
+static struct voltage_map_desc *reg_voltage_map[] = {
+	[MAX77686_LDO1] = &ldo_low_voltage_map_desc,
+	[MAX77686_LDO2] = &ldo_low_voltage_map_desc,
+	[MAX77686_LDO3] = &ldo_voltage_map_desc,
+	[MAX77686_LDO4] = &ldo_voltage_map_desc,
+	[MAX77686_LDO5] = &ldo_voltage_map_desc,
+	[MAX77686_LDO6] = &ldo_low_voltage_map_desc,
+	[MAX77686_LDO7] = &ldo_low_voltage_map_desc,
+	[MAX77686_LDO8] = &ldo_low_voltage_map_desc,
+	[MAX77686_LDO9] = &ldo_voltage_map_desc,
+	[MAX77686_LDO10] = &ldo_voltage_map_desc,
+	[MAX77686_LDO11] = &ldo_voltage_map_desc,
+	[MAX77686_LDO12] = &ldo_voltage_map_desc,
+	[MAX77686_LDO13] = &ldo_voltage_map_desc,
+	[MAX77686_LDO14] = &ldo_voltage_map_desc,
+	[MAX77686_LDO15] = &ldo_low_voltage_map_desc,
+	[MAX77686_LDO16] = &ldo_voltage_map_desc,
+	[MAX77686_LDO17] = &ldo_voltage_map_desc,
+	[MAX77686_LDO18] = &ldo_voltage_map_desc,
+	[MAX77686_LDO19] = &ldo_voltage_map_desc,
+	[MAX77686_LDO20] = &ldo_voltage_map_desc,
+	[MAX77686_LDO21] = &ldo_voltage_map_desc,
+	[MAX77686_LDO22] = &ldo_voltage_map_desc,
+	[MAX77686_LDO23] = &ldo_voltage_map_desc,
+	[MAX77686_LDO24] = &ldo_voltage_map_desc,
+	[MAX77686_LDO25] = &ldo_voltage_map_desc,
+	[MAX77686_LDO26] = &ldo_voltage_map_desc,
+	[MAX77686_BUCK1] = &buck_voltage_map_desc,
+	[MAX77686_BUCK2] = &buck_dvs_voltage_map_desc,
+	[MAX77686_BUCK3] = &buck_dvs_voltage_map_desc,
+	[MAX77686_BUCK4] = &buck_dvs_voltage_map_desc,
+	[MAX77686_BUCK5] = &buck_voltage_map_desc,
+	[MAX77686_BUCK6] = &buck_voltage_map_desc,
+	[MAX77686_BUCK7] = &buck_voltage_map_desc,
+	[MAX77686_BUCK8] = &buck_voltage_map_desc,
+	[MAX77686_BUCK9] = &buck_voltage_map_desc,
+	[MAX77686_EN32KHZ_AP] = NULL,
+	[MAX77686_EN32KHZ_CP] = NULL,
+	[MAX77686_P32KH] = NULL,
+};
+
+static int max77686_list_voltage(struct regulator_dev *rdev,
+		unsigned int selector)
+{
+	const struct voltage_map_desc *desc;
+	int rid = rdev_get_id(rdev);
+	int val;
+
+	if (rid >= ARRAY_SIZE(reg_voltage_map) ||
+			rid < 0)
+		return -EINVAL;
+
+	desc = reg_voltage_map[rid];
+	if (desc == NULL)
+		return -EINVAL;
+
+	val = desc->min + desc->step * selector;
+	if (val > desc->max)
+		return -EINVAL;
+
+	return val;
+}
+
+/*
+ * TODO
+ * Reaction to the LP/Standby for each regulator should be defined by
+ * each consumer, not by the regulator device driver if it depends
+ * on which device is attached to which regulator. Here we are
+ * creating possible PM-wise regression with board changes.Also,
+ * we can do the same effect without creating issues with board
+ * changes by carefully defining .state_mem at bsp and suspend ops
+ * callbacks.
+ */
+unsigned int max77686_opmode_reg[][3] = {
+	/* LDO1 ... LDO26 */
+	/* {NORMAL, LP, STANDBY} */
+	{0x3, 0x2, 0x0}, /* LDO1 */
+	{0x3, 0x2, 0x1},
+	{0x3, 0x2, 0x0},
+	{0x3, 0x2, 0x0},
+	{0x3, 0x2, 0x0},
+	{0x3, 0x2, 0x1},
+	{0x3, 0x2, 0x1},
+	{0x3, 0x2, 0x1},
+	{0x3, 0x2, 0x0},
+	{0x3, 0x2, 0x1},
+	{0x3, 0x2, 0x1}, /* LDO11 */
+	{0x3, 0x2, 0x1},
+	{0x3, 0x2, 0x0},
+	{0x3, 0x2, 0x1},
+	{0x3, 0x2, 0x1},
+	{0x3, 0x2, 0x1},
+	{0x3, 0x2, 0x0},
+	{0x3, 0x2, 0x0},
+	{0x3, 0x2, 0x0},
+	{0x3, 0x2, 0x0},
+	{0x3, 0x2, 0x0}, /* LDO21 */
+	{0x3, 0x2, 0x0},
+	{0x3, 0x2, 0x0},
+	{0x3, 0x2, 0x0},
+	{0x3, 0x2, 0x0},
+	{0x3, 0x2, 0x0},
+	/* BUCK1 ... BUCK9 */
+	{0x3, 0x0, 0x1}, /* BUCK1 */
+	{0x3, 0x2, 0x1},
+	{0x3, 0x2, 0x1},
+	{0x3, 0x2, 0x1},
+	{0x3, 0x0, 0x0},
+	{0x3, 0x0, 0x0},
+	{0x3, 0x0, 0x0},
+	{0x3, 0x0, 0x0},
+	{0x3, 0x0, 0x0},
+	/* 32KHZ */
+	{0x1, 0x0, 0x0},
+	{0x1, 0x0, 0x0},
+	{0x1, 0x0, 0x0},
+};
+
+static int max77686_get_enable_register(struct regulator_dev *rdev,
+		int *reg, int *mask, int *pattern)
+{
+	unsigned int rid = rdev_get_id(rdev);
+	unsigned int mode;
+	struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+
+	if (rid >= ARRAY_SIZE(max77686_opmode_reg))
+		return -EINVAL;
+
+	mode = max77686->opmode_data[rid].mode;
+	pr_debug("%s: rid=%d, mode=%d, size=%d\n",
+		__func__, rid, mode, ARRAY_SIZE(max77686_opmode_reg));
+
+	if (max77686_opmode_reg[rid][mode] == 0x0)
+		WARN(1, "Not supported opmode\n");
+
+	switch (rid) {
+	case MAX77686_LDO1 ... MAX77686_LDO26:
+		*reg = MAX77686_REG_LDO1CTRL1 + (rid - MAX77686_LDO1);
+		*mask = MAX77686_OPMODE_MASK << MAX77686_OPMODE_SHIFT;
+		*pattern = max77686_opmode_reg[rid][mode]
+				<< MAX77686_OPMODE_SHIFT;
+		break;
+	case MAX77686_BUCK1:
+		*reg = MAX77686_REG_BUCK1CTRL;
+		*mask = MAX77686_OPMODE_MASK;
+		*pattern = max77686_opmode_reg[rid][mode];
+		break;
+	case MAX77686_BUCK2:
+	case MAX77686_BUCK3:
+	case MAX77686_BUCK4:
+		*reg = MAX77686_REG_BUCK2CTRL1 + (rid - MAX77686_BUCK2)*10;
+		*mask = MAX77686_OPMODE_MASK << MAX77686_OPMODE_BUCK234_SHIFT;
+		*pattern = max77686_opmode_reg[rid][mode]
+				<< MAX77686_OPMODE_BUCK234_SHIFT;
+		break;
+	case MAX77686_BUCK5 ... MAX77686_BUCK9:
+		*reg = MAX77686_REG_BUCK5CTRL + (rid - MAX77686_BUCK5) * 2;
+		*mask = MAX77686_OPMODE_MASK;
+		*pattern = max77686_opmode_reg[rid][mode];
+		break;
+	case MAX77686_EN32KHZ_AP ... MAX77686_P32KH:
+		*reg = MAX77686_REG_32KHZ;
+		*mask = 0x01 << (rid - MAX77686_EN32KHZ_AP);
+		*pattern = 0x01 << (rid - MAX77686_EN32KHZ_AP);
+		break;
+	default:
+		/* Not controllable or not exists */
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int max77686_reg_is_enabled(struct regulator_dev *rdev)
+{
+	struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+	struct i2c_client *i2c = max77686->iodev->i2c;
+	int ret, reg, mask, pattern;
+	u8 val;
+
+	ret = max77686_get_enable_register(rdev, &reg, &mask, &pattern);
+	if (ret == -EINVAL)
+		return 1; /* "not controllable" */
+	else if (ret)
+		return ret;
+
+	ret = max77686_read_reg(i2c, reg, &val);
+	if (ret)
+		return ret;
+
+	pr_debug("%s: id=%d, ret=%d, val=%x, mask=%x, pattern=%x\n",
+		__func__, rdev_get_id(rdev), (val & mask) == pattern,
+		val, mask, pattern);
+
+	return (val & mask) == pattern;
+}
+
+static int max77686_reg_enable(struct regulator_dev *rdev)
+{
+	struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+	struct i2c_client *i2c = max77686->iodev->i2c;
+	int ret, reg, mask, pattern;
+
+	ret = max77686_get_enable_register(rdev, &reg, &mask, &pattern);
+	if (ret)
+		return ret;
+
+	pr_debug("%s: id=%d, reg=%x, mask=%x, pattern=%x\n",
+		__func__, rdev_get_id(rdev), reg, mask, pattern);
+
+	return max77686_update_reg(i2c, reg, pattern, mask);
+}
+
+static int max77686_reg_disable(struct regulator_dev *rdev)
+{
+	struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+	struct i2c_client *i2c = max77686->iodev->i2c;
+	int ret, reg, mask, pattern;
+
+	ret = max77686_get_enable_register(rdev, &reg, &mask, &pattern);
+	if (ret)
+		return ret;
+
+	pr_debug("%s: id=%d, reg=%x, mask=%x, pattern=%x\n",
+		__func__, rdev_get_id(rdev), reg, mask, pattern);
+
+	return max77686_update_reg(i2c, reg, ~mask, mask);
+}
+
+static int max77686_get_voltage_register(struct regulator_dev *rdev,
+		int *_reg, int *_shift, int *_mask)
+{
+	int rid = rdev_get_id(rdev);
+	int reg, shift = 0, mask = 0x3f;
+
+	switch (rid) {
+	case MAX77686_LDO1 ... MAX77686_LDO26:
+		reg = MAX77686_REG_LDO1CTRL1 + (rid - MAX77686_LDO1);
+		break;
+	case MAX77686_BUCK1:
+		reg = MAX77686_REG_BUCK1OUT;
+		break;
+	case MAX77686_BUCK2:
+		reg = MAX77686_REG_BUCK2DVS1;
+		mask = 0xff;
+		break;
+	case MAX77686_BUCK3:
+		reg = MAX77686_REG_BUCK3DVS1;
+		mask = 0xff;
+		break;
+	case MAX77686_BUCK4:
+		reg = MAX77686_REG_BUCK4DVS1;
+		mask = 0xff;
+		break;
+	case MAX77686_BUCK5 ... MAX77686_BUCK9:
+		reg = MAX77686_REG_BUCK5OUT + (rid - MAX77686_BUCK5) * 2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*_reg = reg;
+	*_shift = shift;
+	*_mask = mask;
+
+	return 0;
+}
+
+static int max77686_get_voltage(struct regulator_dev *rdev)
+{
+	struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+	struct i2c_client *i2c = max77686->iodev->i2c;
+	int reg, shift, mask, ret;
+	int rid = rdev_get_id(rdev);
+	u8 val;
+
+	ret = max77686_get_voltage_register(rdev, &reg, &shift, &mask);
+	if (ret)
+		return ret;
+
+	if ((rid == MAX77686_BUCK2 && max77686->buck2_gpiodvs) ||
+	    (rid == MAX77686_BUCK3 && max77686->buck3_gpiodvs) ||
+	    (rid == MAX77686_BUCK4 && max77686->buck4_gpiodvs))
+		reg += max77686->buck234_gpioindex;
+
+	ret = max77686_read_reg(i2c, reg, &val);
+	if (ret)
+		return ret;
+
+	val >>= shift;
+	val &= mask;
+
+	pr_debug("%s: id=%d, reg=%x, mask=%x, val=%x\n",
+		__func__, rid, reg, mask, val);
+
+	return max77686_list_voltage(rdev, val);
+}
+
+static inline int max77686_get_voltage_proper_val(
+		const struct voltage_map_desc *desc,
+		int min_vol, int max_vol)
+{
+	int i = 0;
+
+	if (desc == NULL)
+		return -EINVAL;
+
+	if (max_vol < desc->min || min_vol > desc->max)
+		return -EINVAL;
+
+	while (desc->min + desc->step * i < min_vol &&
+			desc->min + desc->step * i < desc->max)
+		i++;
+
+	if (desc->min + desc->step * i > max_vol)
+		return -EINVAL;
+
+	if (i >= (1 << desc->n_bits))
+		return -EINVAL;
+
+	return i;
+}
+
+static int max77686_set_voltage(struct regulator_dev *rdev,
+		int min_uV, int max_uV, unsigned *selector)
+{
+	struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+	struct i2c_client *i2c = max77686->iodev->i2c;
+	const struct voltage_map_desc *desc;
+	int rid = rdev_get_id(rdev);
+	int reg, shift = 0, mask, ret;
+	int i;
+	u8 org;
+
+	desc = reg_voltage_map[rid];
+
+	/* W/A code about voltage drop of PASS1 */
+	if (max77686->version < VERSION_MAX77686A &&
+			max77686->chip_rev <= CHIPREV_MAX77686_PASS1) {
+		if (rid >= MAX77686_BUCK1 && rid <= MAX77686_BUCK4) {
+			min_uV = min_uV + MAX77686_DVS_VOL_COMP;
+			max_uV = max_uV + MAX77686_DVS_VOL_COMP;
+		}
+	}
+
+	i = max77686_get_voltage_proper_val(desc, min_uV, max_uV);
+	if (i < 0)
+		return i;
+
+	ret = max77686_get_voltage_register(rdev, &reg, &shift, &mask);
+	if (ret)
+		return ret;
+
+	/* TODO: If GPIO-DVS is being used, this won't work. */
+
+	max77686_read_reg(i2c, reg, &org);
+	org = (org & mask) >> shift;
+
+	pr_debug("%s: id=%d, reg=%x, mask=%x, org=%x, val=%x\n",
+		__func__, rdev_get_id(rdev), reg, mask, org, i);
+
+	ret = max77686_update_reg(i2c, reg, i << shift, mask << shift);
+	*selector = i;
+
+	switch (rid) {
+	case MAX77686_BUCK2 ... MAX77686_BUCK4:
+		if (org < i)
+			udelay(DIV_ROUND_UP(desc->step * (i - org),
+						max77686->ramp_delay * 1000));
+		break;
+	case MAX77686_BUCK1:
+	case MAX77686_BUCK5 ... MAX77686_BUCK9:
+		/* Unconditionally 100 mV/us */
+		if (org < i)
+			udelay(DIV_ROUND_UP(desc->step * (i - org), 100000));
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static int max77686_reg_do_nothing(struct regulator_dev *rdev)
+{
+	return 0;
+}
+
+static struct regulator_ops max77686_ldo_ops = {
+	.list_voltage		= max77686_list_voltage,
+	.is_enabled		= max77686_reg_is_enabled,
+	.enable			= max77686_reg_enable,
+	.disable		= max77686_reg_disable,
+	.get_voltage		= max77686_get_voltage,
+	.set_voltage		= max77686_set_voltage,
+	/* TODO: set 0xC0 -> 0x40 with suspend_enable. for 0x0, ->0x0 */
+	.set_suspend_enable	= max77686_reg_do_nothing,
+	/* LDO's ON(0xC0) means "ON at normal, OFF at suspend" */
+	.set_suspend_disable	= max77686_reg_do_nothing,
+};
+
+static struct regulator_ops max77686_buck_ops = {
+	.list_voltage		= max77686_list_voltage,
+	.is_enabled		= max77686_reg_is_enabled,
+	.enable			= max77686_reg_enable,
+	.disable		= max77686_reg_disable,
+	.get_voltage		= max77686_get_voltage,
+	.set_voltage		= max77686_set_voltage,
+	/* Interpret suspend_enable as "keep on if it was enabled." */
+	.set_suspend_enable	= max77686_reg_do_nothing,
+	.set_suspend_disable	= max77686_reg_disable,
+};
+
+static struct regulator_ops max77686_fixedvolt_ops = {
+	.list_voltage		= max77686_list_voltage,
+	.is_enabled		= max77686_reg_is_enabled,
+	.enable			= max77686_reg_enable,
+	.disable		= max77686_reg_disable,
+	/* Interpret suspend_enable as "keep on if it was enabled." */
+	.set_suspend_enable	= max77686_reg_do_nothing,
+	.set_suspend_disable	= max77686_reg_disable,
+};
+
+#define regulator_desc_ldo(num)		{	\
+	.name		= "LDO"#num,		\
+	.id		= MAX77686_LDO##num,	\
+	.ops		= &max77686_ldo_ops,	\
+	.type		= REGULATOR_VOLTAGE,	\
+	.owner		= THIS_MODULE,		\
+}
+#define regulator_desc_buck(num)		{	\
+	.name		= "BUCK"#num,		\
+	.id		= MAX77686_BUCK##num,	\
+	.ops		= &max77686_buck_ops,	\
+	.type		= REGULATOR_VOLTAGE,	\
+	.owner		= THIS_MODULE,		\
+}
+
+static struct regulator_desc regulators[] = {
+	regulator_desc_ldo(1),
+	regulator_desc_ldo(2),
+	regulator_desc_ldo(3),
+	regulator_desc_ldo(4),
+	regulator_desc_ldo(5),
+	regulator_desc_ldo(6),
+	regulator_desc_ldo(7),
+	regulator_desc_ldo(8),
+	regulator_desc_ldo(9),
+	regulator_desc_ldo(10),
+	regulator_desc_ldo(11),
+	regulator_desc_ldo(12),
+	regulator_desc_ldo(13),
+	regulator_desc_ldo(14),
+	regulator_desc_ldo(15),
+	regulator_desc_ldo(16),
+	regulator_desc_ldo(17),
+	regulator_desc_ldo(18),
+	regulator_desc_ldo(19),
+	regulator_desc_ldo(20),
+	regulator_desc_ldo(21),
+	regulator_desc_ldo(22),
+	regulator_desc_ldo(23),
+	regulator_desc_ldo(24),
+	regulator_desc_ldo(25),
+	regulator_desc_ldo(26),
+	regulator_desc_buck(1),
+	regulator_desc_buck(2),
+	regulator_desc_buck(3),
+	regulator_desc_buck(4),
+	regulator_desc_buck(5),
+	regulator_desc_buck(6),
+	regulator_desc_buck(7),
+	regulator_desc_buck(8),
+	regulator_desc_buck(9),
+	{
+		.name	= "EN32KHz AP",
+		.id	= MAX77686_EN32KHZ_AP,
+		.ops	= &max77686_fixedvolt_ops,
+		.type	= REGULATOR_VOLTAGE,
+		.owner	= THIS_MODULE,
+	}, {
+		.name	= "EN32KHz CP",
+		.id	= MAX77686_EN32KHZ_CP,
+		.ops	= &max77686_fixedvolt_ops,
+		.type	= REGULATOR_VOLTAGE,
+		.owner	= THIS_MODULE,
+	}, {
+		.name	= "EN32KHz PMIC",
+		.id	= MAX77686_P32KH,
+		.ops	= &max77686_fixedvolt_ops,
+		.type	= REGULATOR_VOLTAGE,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int max77686_set_ramp_rate(struct i2c_client *i2c, int rate)
+{
+	int ramp_delay = 0;
+	u8 data = 0;
+
+	switch (rate) {
+	case MAX77686_RAMP_RATE_100MV:
+		ramp_delay = 100;
+		data = MAX77686_REG_RAMP_RATE_100MV;
+		break;
+	case MAX77686_RAMP_RATE_13MV:
+		ramp_delay = 14;
+		data = MAX77686_REG_RAMP_RATE_13MV;
+		break;
+	case MAX77686_RAMP_RATE_27MV:
+		ramp_delay = 28;
+		data = MAX77686_REG_RAMP_RATE_27MV;
+		break;
+	case MAX77686_RAMP_RATE_55MV:
+		ramp_delay = 55;
+		data = MAX77686_REG_RAMP_RATE_55MV;
+		break;
+	}
+
+	pr_debug("%s: ramp_delay=%d, data=0x%x\n", __func__,
+			ramp_delay, data);
+
+	max77686_update_reg(i2c, MAX77686_REG_BUCK2CTRL1, data, 0xC0);
+	max77686_update_reg(i2c, MAX77686_REG_BUCK3CTRL1, data, 0xC0);
+	max77686_update_reg(i2c, MAX77686_REG_BUCK4CTRL1, data, 0xC0);
+
+	return ramp_delay;
+}
+
+static __devinit void max77686_show_pwron_src(struct max77686_data *max77686)
+{
+	const char *src[] = {
+		"PWRON=High", "JIGONB=Low", "ACOKB=Low", "Manual Reset Event",
+		"ALARM1", "ALARM2", "SMPL Event", "WTSR Event"
+	};
+	struct i2c_client *i2c = max77686->iodev->i2c;
+	int i, ret;
+	u8 data = 0;
+
+	ret = max77686_read_reg(i2c, MAX77686_REG_PWRON, &data);
+	if (ret < 0) {
+		dev_err(max77686->dev, "%s: fail to read PWRON reg(%d)\n",
+				__func__, ret);
+		return;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(src); i++)
+		if (data & 1 << i)
+			dev_info(max77686->dev, "Power on triggered by %s\n",
+					src[i]);
+}
+
+static __devinit int max77686_pmic_probe(struct platform_device *pdev)
+{
+	struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+	struct max77686_platform_data *pdata = dev_get_platdata(iodev->dev);
+	struct regulator_dev **rdev;
+	struct max77686_data *max77686;
+	struct i2c_client *i2c;
+	int i, ret, size;
+	u8 data = 0;
+
+	pr_debug("%s\n", __func__);
+
+	if (!pdata) {
+		dev_err(pdev->dev.parent, "No platform init data supplied.\n");
+		return -ENODEV;
+	}
+
+	max77686 = kzalloc(sizeof(struct max77686_data), GFP_KERNEL);
+	if (!max77686)
+		return -ENOMEM;
+
+	size = sizeof(struct regulator_dev *) * pdata->num_regulators;
+	max77686->rdev = kzalloc(size, GFP_KERNEL);
+	if (!max77686->rdev) {
+		kfree(max77686);
+		return -ENOMEM;
+	}
+
+	rdev = max77686->rdev;
+	max77686->dev = &pdev->dev;
+	max77686->iodev = iodev;
+	max77686->num_regulators = pdata->num_regulators;
+	platform_set_drvdata(pdev, max77686);
+	i2c = max77686->iodev->i2c;
+
+	max77686->opmode_data = pdata->opmode_data;
+	max77686->ramp_delay = max77686_set_ramp_rate(i2c, pdata->ramp_rate);
+
+	max77686_read_reg(i2c, MAX77686_REG_DEVICE_ID, &data);
+	max77686->version =
+		(data & MAX77686_VERSION_MASK) >> __ffs(MAX77686_VERSION_MASK);
+	max77686->chip_rev = data & MAX77686_CHIPREV_MASK;
+	pr_info("%s: Device ID=0x%02x, (version:%d, chip_rev:%d)\n", __func__,
+			data, max77686->version, max77686->chip_rev);
+
+	max77686_show_pwron_src(max77686);
+
+	/*
+	 * TODO
+	 * This disables GPIO-DVS. Later we may need to implement GPIO-DVS..
+	 * or we do not?
+	 */
+	max77686->buck2_gpiodvs = false;
+	max77686->buck3_gpiodvs = false;
+	max77686->buck4_gpiodvs = false;
+	for (i = 0; i < 3; i++) {
+		if (gpio_is_valid(pdata->buck234_gpio_dvs[i])) {
+			max77686->buck234_gpios_dvs_label[i] =
+				kasprintf(GFP_KERNEL, "MAX77686 DVS%d", i);
+			max77686->buck234_gpios_dvs[i] =
+				pdata->buck234_gpio_dvs[i];
+			gpio_request(pdata->buck234_gpio_dvs[i],
+				max77686->buck234_gpios_dvs_label[i]);
+			gpio_direction_output(pdata->buck234_gpio_dvs[i], 0);
+		} else {
+			dev_info(&pdev->dev, "GPIO MAX77686 DVS%d ignored (%d)\n",
+				 i, pdata->buck234_gpio_dvs[i]);
+		}
+
+		if (gpio_is_valid(pdata->buck234_gpio_selb[i])) {
+			max77686->buck234_gpios_selb_label[i] =
+				kasprintf(GFP_KERNEL, "MAX77686 SELB%d", i);
+			max77686->buck234_gpios_selb[i] =
+				pdata->buck234_gpio_selb[i];
+			gpio_request(pdata->buck234_gpio_selb[i],
+				max77686->buck234_gpios_selb_label[i]);
+			gpio_direction_output(pdata->buck234_gpio_selb[i], 0);
+		} else {
+			dev_info(&pdev->dev, "GPIO MAX77686 SELB%d ignored (%d)\n",
+				 i, pdata->buck234_gpio_selb[i]);
+		}
+	}
+	max77686->buck234_gpioindex = 0;
+
+	for (i = 0; i < 8; i++) {
+		ret = max77686_get_voltage_proper_val(
+				&buck_dvs_voltage_map_desc,
+				pdata->buck2_voltage[i],
+				pdata->buck2_voltage[i]
+					+ buck_dvs_voltage_map_desc.step);
+		/* 1.1V as default for safety */
+		if (ret < 0)
+			max77686->buck2_vol[i] = 0x28;
+		else
+			max77686->buck2_vol[i] = ret;
+		max77686_write_reg(i2c, MAX77686_REG_BUCK2DVS1 + i,
+				   max77686->buck2_vol[i]);
+
+		ret = max77686_get_voltage_proper_val(
+				&buck_dvs_voltage_map_desc,
+				pdata->buck3_voltage[i],
+				pdata->buck3_voltage[i]
+					+ buck_dvs_voltage_map_desc.step);
+		/* 1.1V as default for safety */
+		if (ret < 0)
+			max77686->buck3_vol[i] = 0x28;
+		else
+			max77686->buck3_vol[i] = ret;
+		max77686_write_reg(i2c, MAX77686_REG_BUCK3DVS1 + i,
+				   max77686->buck3_vol[i]);
+
+		ret = max77686_get_voltage_proper_val(
+				&buck_dvs_voltage_map_desc,
+				pdata->buck4_voltage[i],
+				pdata->buck4_voltage[i]
+					+ buck_dvs_voltage_map_desc.step);
+		/* 1.1V as default for safety */
+		if (ret < 0)
+			max77686->buck4_vol[i] = 0x28;
+		else
+			max77686->buck4_vol[i] = ret;
+		max77686_write_reg(i2c, MAX77686_REG_BUCK4DVS1 + i,
+				   max77686->buck4_vol[i]);
+	}
+
+	if (pdata->has_full_constraints)
+		regulator_has_full_constraints();
+
+	if (max77686->version > VERSION_MAX77686)
+		reg_voltage_map[MAX77686_BUCK1] = &buck1_voltage_map_desc;
+
+	for (i = 0; i < pdata->num_regulators; i++) {
+		const struct voltage_map_desc *desc;
+		int id = pdata->regulators[i].id;
+
+		desc = reg_voltage_map[id];
+		if (desc) {
+			regulators[id].n_voltages =
+				(desc->max - desc->min) / desc->step + 1;
+
+			pr_debug("%s: desc=%p, id=%d, n_vol=%d, "
+					"max=%d, min=%d, step=%d\n", __func__,
+					desc, id, regulators[id].n_voltages,
+					desc->max, desc->min, desc->step);
+		}
+
+		rdev[i] = regulator_register(&regulators[id], max77686->dev,
+				pdata->regulators[i].initdata, max77686, NULL);
+		if (IS_ERR(rdev[i])) {
+			ret = PTR_ERR(rdev[i]);
+			dev_err(max77686->dev, "regulator init failed for %d\n",
+					id);
+			rdev[i] = NULL;
+			goto err;
+		}
+	}
+
+	return 0;
+err:
+	for (i = 0; i < 3; i++) {
+		if (max77686->buck234_gpios_dvs[i])
+			gpio_free(max77686->buck234_gpios_dvs[i]);
+		if (max77686->buck234_gpios_dvs[i])
+			gpio_free(max77686->buck234_gpios_selb[i]);
+		kfree(max77686->buck234_gpios_dvs_label[i]);
+		kfree(max77686->buck234_gpios_selb_label[i]);
+	}
+
+	for (i = 0; i < max77686->num_regulators; i++)
+		if (rdev[i])
+			regulator_unregister(rdev[i]);
+
+	kfree(max77686->rdev);
+	kfree(max77686);
+
+	return ret;
+}
+
+static int __devexit max77686_pmic_remove(struct platform_device *pdev)
+{
+	struct max77686_data *max77686 = platform_get_drvdata(pdev);
+	struct regulator_dev **rdev = max77686->rdev;
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		if (max77686->buck234_gpios_dvs[i])
+			gpio_free(max77686->buck234_gpios_dvs[i]);
+		if (max77686->buck234_gpios_dvs[i])
+			gpio_free(max77686->buck234_gpios_selb[i]);
+		kfree(max77686->buck234_gpios_dvs_label[i]);
+		kfree(max77686->buck234_gpios_selb_label[i]);
+	}
+
+	for (i = 0; i < max77686->num_regulators; i++)
+		if (rdev[i])
+			regulator_unregister(rdev[i]);
+
+	kfree(max77686->rdev);
+	kfree(max77686);
+
+	return 0;
+}
+
+static const struct platform_device_id max77686_pmic_id[] = {
+	{ "max77686-pmic", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, max77686_pmic_id);
+
+static struct platform_driver max77686_pmic_driver = {
+	.driver = {
+		.name = "max77686-pmic",
+		.owner = THIS_MODULE,
+	},
+	.probe = max77686_pmic_probe,
+	.remove = __devexit_p(max77686_pmic_remove),
+	.id_table = max77686_pmic_id,
+};
+
+static int __init max77686_pmic_init(void)
+{
+	pr_debug("%s\n", __func__);
+
+	return platform_driver_register(&max77686_pmic_driver);
+}
+subsys_initcall(max77686_pmic_init);
+
+static void __exit max77686_pmic_cleanup(void)
+{
+	platform_driver_unregister(&max77686_pmic_driver);
+}
+module_exit(max77686_pmic_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 77686 Regulator Driver");
+MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 8c8377d..a6092a6 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -213,6 +213,16 @@
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-max8998.
 
+config RTC_DRV_MAX77686
+	tristate "Maxim MAX77686"
+	depends on MFD_MAX77686
+	help
+	  If you say yes here you will get support for the
+	  RTC of Maxim MAX77686 PMIC.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-max77686.
+
 config RTC_DRV_RS5C372
 	tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A"
 	help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 727ae77..e82b9c1 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -65,6 +65,7 @@
 obj-$(CONFIG_RTC_DRV_MAX6900)	+= rtc-max6900.o
 obj-$(CONFIG_RTC_DRV_MAX8925)	+= rtc-max8925.o
 obj-$(CONFIG_RTC_DRV_MAX8998)	+= rtc-max8998.o
+obj-$(CONFIG_RTC_DRV_MAX77686)	+= rtc-max77686.o
 obj-$(CONFIG_RTC_DRV_MAX6902)	+= rtc-max6902.o
 obj-$(CONFIG_RTC_DRV_MC13XXX)	+= rtc-mc13xxx.o
 obj-$(CONFIG_RTC_DRV_MSM6242)	+= rtc-msm6242.o
diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c
new file mode 100644
index 0000000..e90636a
--- /dev/null
+++ b/drivers/rtc/rtc-max77686.c
@@ -0,0 +1,591 @@
+/*
+ * RTC driver for Maxim MAX77686
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.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/rtc.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+
+/* RTCINTM Register */
+#define RTCA1M_SHIFT			1
+#define RTCA1M_MASK			(1 << RTCA1M_SHIFT)
+/* RTCCNTL Register */
+#define BCD_EN_SHIFT			0
+#define BCD_EN_MASK			(1 << BCD_EN_SHIFT)
+#define MODEL24_SHIFT			1
+#define MODEL24_MASK			(1 << MODEL24_SHIFT)
+/* RTCUPDATE0 Register */
+#define RTC_UDR_SHIFT			0
+#define RTC_UDR_MASK			(1 << RTC_UDR_SHIFT)
+#define RTC_RBUDR_SHIFT			4
+#define RTC_RBUDR_MASK			(1 << RTC_RBUDR_SHIFT)
+/* WTSR and SMPL Register */
+#define WTSRT_SHIFT			0
+#define SMPLT_SHIFT			2
+#define WTSR_EN_SHIFT			6
+#define SMPL_EN_SHIFT			7
+#define WTSRT_MASK			(3 << WTSRT_SHIFT)
+#define SMPLT_MASK			(3 << SMPLT_SHIFT)
+#define WTSR_EN_MASK			(1 << WTSR_EN_SHIFT)
+#define SMPL_EN_MASK			(1 << SMPL_EN_SHIFT)
+/* RTCHOUR register */
+#define HOUR_PM_SHIFT			6
+#define HOUR_PM_MASK			(1 << HOUR_PM_SHIFT)
+/* RTC Alarm Enable */
+#define ALARM_ENABLE_SHIFT		7
+#define ALARM_ENABLE_MASK		(1 << ALARM_ENABLE_SHIFT)
+
+/* PMIC STATUS1 register */
+#define STATUS1_JIGONB_MASK		BIT(1)
+/* PMIC STATUS2 register */
+#define STATUS2_RTCA1_MASK		BIT(2)
+
+#define MAX77686_RTC_UPDATE_DELAY	16
+
+#define WTSR_TIMER_BITS(v)		(((v) << WTSRT_SHIFT) & WTSRT_MASK)
+#define SMPL_TIMER_BITS(v)		(((v) << SMPLT_SHIFT) & SMPLT_MASK)
+
+/* RTC Counter Register offsets */
+enum rtc_cnt_reg_offset {
+	RTC_SEC = 0,
+	RTC_MIN,
+	RTC_HOUR,
+	RTC_WEEKDAY,
+	RTC_MONTH,
+	RTC_YEAR,
+	RTC_DATE,
+	NR_RTC_CNT_REGS,
+};
+
+struct max77686_rtc_info {
+	struct device		*dev;
+	struct max77686_dev	*max77686;
+	struct i2c_client	*rtc;
+	struct rtc_device	*rtc_dev;
+	struct mutex		lock;
+	int			irq;
+	bool			use_irq;
+	bool			wtsr_en;
+	bool			alarm_enabled;
+	u8			update0_reg;
+};
+
+enum MAX77686_RTC_OP {
+	MAX77686_RTC_WRITE,
+	MAX77686_RTC_READ,
+};
+
+
+static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm)
+{
+	tm->tm_sec = data[RTC_SEC] & 0x7f;
+	tm->tm_min = data[RTC_MIN] & 0x7f;
+	tm->tm_hour = data[RTC_HOUR] & 0x1f;
+	tm->tm_wday = __fls(data[RTC_WEEKDAY] & 0x7f);
+	tm->tm_mday = data[RTC_DATE] & 0x1f;
+	tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
+	tm->tm_year = (data[RTC_YEAR] & 0x7f) + 100;
+	tm->tm_yday = 0;
+	tm->tm_isdst = 0;
+}
+
+static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data)
+{
+	data[RTC_SEC] = tm->tm_sec;
+	data[RTC_MIN] = tm->tm_min;
+	data[RTC_HOUR] = tm->tm_hour;
+	data[RTC_WEEKDAY] = 1 << tm->tm_wday;
+	data[RTC_DATE] = tm->tm_mday;
+	data[RTC_MONTH] = tm->tm_mon + 1;
+	data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0;
+
+	if (tm->tm_year < 100) {
+		pr_warn("%s: MAX77686 RTC cannot handle the year %d\n",
+				__func__, 1900 + tm->tm_year);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int max77686_rtc_update(struct max77686_rtc_info *info,
+				enum MAX77686_RTC_OP op)
+{
+	u8 data;
+	int ret;
+
+	if (!info || !info->rtc) {
+		pr_err("%s: Invalid argument\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (op) {
+	case MAX77686_RTC_WRITE:
+		data = RTC_UDR_MASK;
+		break;
+	case MAX77686_RTC_READ:
+		data = RTC_RBUDR_MASK;
+		break;
+	default:
+		dev_err(info->dev, "%s: invalid op(%d)\n", __func__, op);
+		return -EINVAL;
+	}
+
+	data |= info->update0_reg;
+
+	/* NOTES about UDF and RBUDF(RTCUPDATE1(0x05) register):
+	 * If the user read RTCUPDATE1 register when RBUDF or UDF bit of the
+	 * register was set to 1 at the same time, the value read from the
+	 * register could be 0 and RBUDF or UDF bit would be cleared.
+	 * The user should wait for 16msec before initiating new read/write
+	 * operation and RTCUPDATE1 register will be erased from the datasheet.
+	 */
+	ret = max77686_write_reg(info->rtc, MAX77686_RTC_UPDATE0, data);
+	if (ret < 0)
+		dev_err(info->dev,
+			"%s: fail to write update0 reg(ret=%d, data=0x%x)\n",
+			__func__, ret, data);
+	else
+		msleep(MAX77686_RTC_UPDATE_DELAY);
+
+	return ret;
+}
+
+static int max77686_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct max77686_rtc_info *info = dev_get_drvdata(dev);
+	u8 data[NR_RTC_CNT_REGS];
+	int ret;
+
+	mutex_lock(&info->lock);
+	ret = max77686_rtc_update(info, MAX77686_RTC_READ);
+	if (ret < 0)
+		goto out;
+
+	ret = max77686_bulk_read(info->rtc, MAX77686_RTC_SEC, NR_RTC_CNT_REGS,
+			data);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__,
+			ret);
+		goto out;
+	}
+
+	dev_dbg(info->dev, "%s: %d-%02d-%02d %02d:%02d:%02d(0x%02x)\n",
+			__func__, data[RTC_YEAR] + 2000, data[RTC_MONTH],
+			data[RTC_DATE], data[RTC_HOUR], data[RTC_MIN],
+			data[RTC_SEC], data[RTC_WEEKDAY]);
+
+	max77686_rtc_data_to_tm(data, tm);
+	ret = rtc_valid_tm(tm);
+out:
+	mutex_unlock(&info->lock);
+	return ret;
+}
+
+static int max77686_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct max77686_rtc_info *info = dev_get_drvdata(dev);
+	u8 data[NR_RTC_CNT_REGS];
+	int ret;
+
+	ret = max77686_rtc_tm_to_data(tm, data);
+	if (ret < 0)
+		return ret;
+
+	dev_dbg(info->dev, "%s: %d-%02d-%02d %02d:%02d:%02d(0x%02x)\n",
+			__func__, data[RTC_YEAR] + 2000, data[RTC_MONTH],
+			data[RTC_DATE], data[RTC_HOUR], data[RTC_MIN],
+			data[RTC_SEC], data[RTC_WEEKDAY]);
+
+	mutex_lock(&info->lock);
+	ret = max77686_bulk_write(info->rtc, MAX77686_RTC_SEC, NR_RTC_CNT_REGS,
+			data);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__,
+			ret);
+		goto out;
+	}
+
+	ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
+out:
+	mutex_unlock(&info->lock);
+	return ret;
+}
+
+static int max77686_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct max77686_rtc_info *info = dev_get_drvdata(dev);
+	u8 data[NR_RTC_CNT_REGS], val;
+	int ret;
+
+	mutex_lock(&info->lock);
+	ret = max77686_rtc_update(info, MAX77686_RTC_READ);
+	if (ret < 0)
+		goto out;
+
+	ret = max77686_bulk_read(info->rtc, MAX77686_ALARM1_SEC,
+			NR_RTC_CNT_REGS, data);
+	if (ret < 0) {
+		dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n",
+			__func__, __LINE__, ret);
+		goto out;
+	}
+
+	max77686_rtc_data_to_tm(data, &alrm->time);
+
+	dev_dbg(info->dev, "%s: %d-%02d-%02d %02d:%02d:%02d(%d)\n", __func__,
+			alrm->time.tm_year + 1900, alrm->time.tm_mon + 1,
+			alrm->time.tm_mday, alrm->time.tm_hour,
+			alrm->time.tm_min, alrm->time.tm_sec,
+			alrm->time.tm_wday);
+
+	alrm->enabled = info->alarm_enabled;
+	alrm->pending = 0;
+	ret = max77686_read_reg(info->max77686->i2c, MAX77686_REG_STATUS2,
+			&val);
+	if (ret < 0) {
+		dev_err(info->dev, "%s:%d fail to read status1 reg(%d)\n",
+			__func__, __LINE__, ret);
+		goto out;
+	}
+	if (val & STATUS2_RTCA1_MASK)
+		alrm->pending = 1;
+out:
+	mutex_unlock(&info->lock);
+	return ret;
+}
+
+static int max77686_rtc_set_alarm_enable(struct max77686_rtc_info *info,
+					 bool enabled)
+{
+	if (!info->use_irq)
+		return -EPERM;
+
+	if (enabled && !info->alarm_enabled) {
+		info->alarm_enabled = true;
+		enable_irq(info->irq);
+	} else if (!enabled && info->alarm_enabled) {
+		info->alarm_enabled = false;
+		disable_irq(info->irq);
+	}
+	return 0;
+}
+
+static int max77686_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct max77686_rtc_info *info = dev_get_drvdata(dev);
+	u8 data[NR_RTC_CNT_REGS];
+	int ret, i;
+
+	mutex_lock(&info->lock);
+	ret = max77686_rtc_tm_to_data(&alrm->time, data);
+	if (ret < 0)
+		goto out;
+
+	dev_dbg(info->dev, "%s: %d-%02d-%02d %02d:%02d:%02d(0x%02x)\n",
+			__func__, data[RTC_YEAR] + 2000, data[RTC_MONTH],
+			data[RTC_DATE], data[RTC_HOUR], data[RTC_MIN],
+			data[RTC_SEC], data[RTC_WEEKDAY]);
+
+	for (i = 0; i < NR_RTC_CNT_REGS; i++)
+		data[i] |= ALARM_ENABLE_MASK;
+
+	ret = max77686_bulk_write(info->rtc, MAX77686_ALARM1_SEC,
+			NR_RTC_CNT_REGS, data);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+			__func__, ret);
+		goto out;
+	}
+
+	ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
+	if (ret < 0)
+		goto out;
+
+	ret = max77686_rtc_set_alarm_enable(info, alrm->enabled);
+out:
+	mutex_unlock(&info->lock);
+	return ret;
+}
+
+static int max77686_rtc_alarm_irq_enable(struct device *dev,
+					 unsigned int enabled)
+{
+	struct max77686_rtc_info *info = dev_get_drvdata(dev);
+	int ret;
+
+	mutex_lock(&info->lock);
+	ret = max77686_rtc_set_alarm_enable(info, enabled);
+	mutex_unlock(&info->lock);
+	return ret;
+}
+
+static irqreturn_t max77686_rtc_alarm_irq(int irq, void *data)
+{
+	struct max77686_rtc_info *info = data;
+
+	if (!info->rtc_dev)
+		return IRQ_HANDLED;
+
+	dev_info(info->dev, "%s:irq(%d)\n", __func__, irq);
+
+	rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
+
+	return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops max77686_rtc_ops = {
+	.read_time = max77686_rtc_read_time,
+	.set_time = max77686_rtc_set_time,
+	.read_alarm = max77686_rtc_read_alarm,
+	.set_alarm = max77686_rtc_set_alarm,
+	.alarm_irq_enable = max77686_rtc_alarm_irq_enable,
+};
+
+static bool max77686_is_jigonb_low(struct max77686_rtc_info *info)
+{
+	int ret;
+	u8 val;
+
+	ret = max77686_read_reg(info->max77686->i2c, MAX77686_REG_STATUS1,
+			&val);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to read status1 reg(%d)\n",
+			__func__, ret);
+		return false;
+	}
+
+	return !(val & STATUS1_JIGONB_MASK);
+}
+
+static void __devinit
+max77686_rtc_enable_wtsr_smpl(struct max77686_rtc_info *info,
+			      struct max77686_platform_data *pdata)
+{
+	u8 val;
+	int ret;
+
+	if (pdata->wtsr_smpl->check_jigon && max77686_is_jigonb_low(info))
+		pdata->wtsr_smpl->smpl_en = false;
+
+	val = (pdata->wtsr_smpl->wtsr_en << WTSR_EN_SHIFT)
+		| (pdata->wtsr_smpl->smpl_en << SMPL_EN_SHIFT)
+		| WTSR_TIMER_BITS(pdata->wtsr_smpl->wtsr_timer_val)
+		| SMPL_TIMER_BITS(pdata->wtsr_smpl->smpl_timer_val);
+
+	dev_info(info->dev, "%s: WTSR: %s, SMPL: %s\n", __func__,
+			pdata->wtsr_smpl->wtsr_en ? "enable" : "disable",
+			pdata->wtsr_smpl->smpl_en ? "enable" : "disable");
+
+	ret = max77686_write_reg(info->rtc, MAX77686_WTSR_SMPL_CNTL, val);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to write WTSR/SMPL reg(%d)\n",
+				__func__, ret);
+		return;
+	}
+	info->wtsr_en = pdata->wtsr_smpl->wtsr_en;
+
+	max77686_rtc_update(info, MAX77686_RTC_WRITE);
+}
+
+static void max77686_rtc_disable_wtsr(struct max77686_rtc_info *info)
+{
+	int ret;
+
+	dev_info(info->dev, "%s: disable WTSR\n", __func__);
+	max77686_rtc_update(info, MAX77686_RTC_READ);
+	ret = max77686_update_reg(info->rtc, MAX77686_WTSR_SMPL_CNTL, 0,
+			WTSR_EN_MASK);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n",
+				__func__, ret);
+		return;
+	}
+	max77686_rtc_update(info, MAX77686_RTC_WRITE);
+}
+
+static int __devinit max77686_rtc_init_reg(struct max77686_rtc_info *info,
+					struct max77686_platform_data *pdata)
+{
+	u8 data[2], update0, cntl;
+	int ret;
+
+	max77686_rtc_update(info, MAX77686_RTC_READ);
+
+	ret = max77686_read_reg(info->rtc, MAX77686_RTC_CONTROL, &cntl);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to read control reg(%d)\n",
+			__func__, ret);
+		return ret;
+	}
+
+	ret = max77686_read_reg(info->rtc, MAX77686_RTC_UPDATE0, &update0);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to read update0 reg(%d)\n",
+			__func__, ret);
+		return ret;
+	}
+	info->update0_reg = update0 & ~(RTC_UDR_MASK | RTC_RBUDR_MASK);
+
+	/* If the value of CONTROL register is 0, RTC registers were reset */
+	if (cntl == MODEL24_MASK)
+		return 0;
+
+	/* Set RTC control register : Binary mode, 24hour mode */
+	data[0] = BCD_EN_MASK | MODEL24_MASK;
+	data[1] = MODEL24_MASK;
+
+	ret = max77686_bulk_write(info->rtc, MAX77686_RTC_CONTROLM, 2, data);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
+			__func__, ret);
+		return ret;
+	}
+
+	ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
+	if (ret < 0)
+		return ret;
+
+	if (pdata->init_time) {
+		dev_info(info->dev, "%s: initialize RTC time\n", __func__);
+		ret = max77686_rtc_set_time(info->dev, pdata->init_time);
+	}
+	return ret;
+}
+
+static int __devinit max77686_rtc_probe(struct platform_device *pdev)
+{
+	struct max77686_dev *max77686 = dev_get_drvdata(pdev->dev.parent);
+	struct max77686_platform_data *pdata = dev_get_platdata(max77686->dev);
+	struct max77686_rtc_info *info;
+	int ret;
+
+	if (!pdata) {
+		dev_err(pdev->dev.parent, "RTC: No platform data supplied.\n");
+		return -ENODEV;
+	}
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	mutex_init(&info->lock);
+	info->dev = &pdev->dev;
+	info->max77686 = max77686;
+	info->rtc = max77686->rtc;
+	info->irq = max77686->irq_base + MAX77686_RTCIRQ_RTCA1;
+
+	platform_set_drvdata(pdev, info);
+
+	ret = max77686_rtc_init_reg(info, pdata);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret);
+		goto err_init_reg;
+	}
+
+	if (pdata->wtsr_smpl)
+		max77686_rtc_enable_wtsr_smpl(info, pdata);
+
+	device_init_wakeup(&pdev->dev, 1);
+
+	ret = request_threaded_irq(info->irq, NULL, max77686_rtc_alarm_irq, 0,
+				   "rtc-alarm0", info);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
+			info->irq, ret);
+		goto err_request_irq;
+	}
+	disable_irq(info->irq);
+	disable_irq(info->irq);
+	info->use_irq = true;
+
+	info->rtc_dev = rtc_device_register("max77686-rtc", &pdev->dev,
+					    &max77686_rtc_ops, THIS_MODULE);
+	if (IS_ERR(info->rtc_dev)) {
+		ret = PTR_ERR(info->rtc_dev);
+		dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+		if (!ret)
+			ret = -EINVAL;
+		goto err_rtc_dev_register;
+	}
+	enable_irq(info->irq);
+	return 0;
+
+err_rtc_dev_register:
+	enable_irq(info->irq);
+	enable_irq(info->irq);
+	free_irq(info->irq, info);
+err_request_irq:
+err_init_reg:
+	kfree(info);
+	return ret;
+}
+
+static int __devexit max77686_rtc_remove(struct platform_device *pdev)
+{
+	struct max77686_rtc_info *info = platform_get_drvdata(pdev);
+
+	if (!info->alarm_enabled)
+		enable_irq(info->irq);
+
+	free_irq(info->irq, info);
+	rtc_device_unregister(info->rtc_dev);
+	kfree(info);
+
+	return 0;
+}
+
+static void max77686_rtc_shutdown(struct platform_device *pdev)
+{
+	struct max77686_rtc_info *info = platform_get_drvdata(pdev);
+
+	if (info->wtsr_en)
+		max77686_rtc_disable_wtsr(info);
+}
+
+static const struct platform_device_id rtc_id[] = {
+	{"max77686-rtc", 0},
+	{},
+};
+
+static struct platform_driver max77686_rtc_driver = {
+	.driver = {
+		.name	= "max77686-rtc",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= max77686_rtc_probe,
+	.remove		= __devexit_p(max77686_rtc_remove),
+	.shutdown	= max77686_rtc_shutdown,
+	.id_table	= rtc_id,
+};
+
+static int __init max77686_rtc_init(void)
+{
+	return platform_driver_register(&max77686_rtc_driver);
+}
+
+module_init(max77686_rtc_init);
+
+static void __exit max77686_rtc_exit(void)
+{
+	platform_driver_unregister(&max77686_rtc_driver);
+}
+
+module_exit(max77686_rtc_exit);
+
+MODULE_DESCRIPTION("Maxim MAX77686 RTC driver");
+MODULE_AUTHOR("<ms925.kim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index fe1586718..9d36c53 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -75,6 +75,7 @@
 source "drivers/staging/iio/light/Kconfig"
 source "drivers/staging/iio/magnetometer/Kconfig"
 source "drivers/staging/iio/meter/Kconfig"
+source "drivers/staging/iio/pressure/Kconfig"
 source "drivers/staging/iio/resolver/Kconfig"
 source "drivers/staging/iio/trigger/Kconfig"
 
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
index 5075291..c4d27cc 100644
--- a/drivers/staging/iio/Makefile
+++ b/drivers/staging/iio/Makefile
@@ -31,5 +31,6 @@
 obj-y += light/
 obj-y += magnetometer/
 obj-y += meter/
+obj-y += pressure/
 obj-y += resolver/
 obj-y += trigger/
diff --git a/drivers/staging/iio/imu/Kconfig b/drivers/staging/iio/imu/Kconfig
index 2c2f47d..e2fa886 100644
--- a/drivers/staging/iio/imu/Kconfig
+++ b/drivers/staging/iio/imu/Kconfig
@@ -14,4 +14,5 @@
 	  adis16365, adis16400 and adis16405 triaxial inertial sensors
 	  (adis16400 series also have magnetometers).
 
+source "drivers/staging/iio/imu/mpu/Kconfig"
 endmenu
diff --git a/drivers/staging/iio/imu/Makefile b/drivers/staging/iio/imu/Makefile
index 3400a13..9c471fd 100644
--- a/drivers/staging/iio/imu/Makefile
+++ b/drivers/staging/iio/imu/Makefile
@@ -5,3 +5,5 @@
 adis16400-y             := adis16400_core.o
 adis16400-$(CONFIG_IIO_BUFFER) += adis16400_ring.o adis16400_trigger.o
 obj-$(CONFIG_ADIS16400) += adis16400.o
+
+obj-y += mpu/
diff --git a/drivers/staging/iio/imu/mpu/Kconfig b/drivers/staging/iio/imu/mpu/Kconfig
new file mode 100644
index 0000000..fe29936
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu/Kconfig
@@ -0,0 +1,31 @@
+#
+# inv-mpu-iio driver for Invensense MPU devices and combos
+#
+
+config INV_MPU_IIO
+	tristate "Invensense MPU devices"
+	depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF && IIO_TRIGGER && !INV_MPU
+	default n
+	help
+	  This driver supports the Invensense MPU devices.
+	  This includes MPU6050/MPU3050/MPU9150/ITG3500.
+	  This driver can be built as a module. The module will be called
+	  inv-mpu-iio.
+
+config INV_IIO_MPU3050_ACCEL_SLAVE_BMA250
+	bool  "Invense MPU3050 slave accelerometer device for bma250"
+	depends on INV_MPU_IIO
+	default n
+	help
+	  This is slave device enable MPU3050 accelerometer slave device.
+	  Right now, it is only bma250. For other acceleromter device,
+	  it can be added to this menu if the proper interface is filled.
+	  There are some interface function to be defined.
+
+config INV_TESTING
+    boolean "Invensense MPU Testing Information"
+    depends on INV_MPU_IIO
+    default n
+    help
+      This flag enables display of additional testing information from the
+      Invensense MPU IIO driver
diff --git a/drivers/staging/iio/imu/mpu/Makefile b/drivers/staging/iio/imu/mpu/Makefile
new file mode 100644
index 0000000..6ce3240
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu/Makefile
@@ -0,0 +1,19 @@
+#
+# Makefile for Invensense inv-mpu-iio device.
+#
+
+obj-$(CONFIG_INV_MPU_IIO) += inv-mpu-iio.o
+
+inv-mpu-iio-objs := inv_mpu_core.o
+inv-mpu-iio-objs += inv_mpu_ring.o
+inv-mpu-iio-objs += inv_mpu_trigger.o
+inv-mpu-iio-objs += inv_mpu_misc.o
+inv-mpu-iio-objs += inv_mpu3050_iio.o
+inv-mpu-iio-objs += dmpDefaultMPU6050.o
+
+# the Bosch BMA250 driver is added to the inv-mpu device driver because it
+# must be connected to an MPU3050 device on the secondary slave bus.
+ifeq ($(CONFIG_INV_IIO_MPU3050_ACCEL_SLAVE_BMA250), y)
+inv-mpu-iio-objs += inv_slave_bma250.o
+endif
+
diff --git a/drivers/staging/iio/imu/mpu/README b/drivers/staging/iio/imu/mpu/README
new file mode 100644
index 0000000..a896fb9
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu/README
@@ -0,0 +1,500 @@
+Kernel driver inv-mpu-iio
+Author: Invensense <http://invensense.com>
+
+Table of Contents:
+==================
+- Description
+- Integrating the Driver in the Linux Kernel
+- Board and Platform Data
+	- Interrupt Pin
+	- Platform Data
+- Board File Modifications for Secondary I2C Configuration
+	- MPU-6050 + AKM8963 on the secondary I2C interface
+	- MPU-6500 + AKM8963 on the secondary I2C interface
+	- MPU-9150
+	- MPU-3050 + BMA250 on the secondary I2C interface
+- Board File Modifications for Invensense Devices
+	- MPU-3050
+	- ITG-3500
+	- MPU-6050
+	- MPU-6500
+	- MPU-9150
+- IIO Subsystem
+	- Communicating with the Driver in Userspace
+	- ITG-3500
+	- MPU-6050 and MPU-6500
+	- MPU-9150
+	- MPU-3050 + BMA250 on the secondary I2C interface
+- Low Power Accelerometer Mode
+- DMP Event
+- Streaming Data to an Userspace Application
+- Recommended Sysfs Entry Setup Sequence
+	- With DMP Firmware
+	- Without DMP Firmware
+- Test Applications
+	- Running Test Applications with MPU-9150/MPU-6050/MPU-6500
+	- Running Test Applications with MPU-3050/ITG-3500
+
+
+
+Description
+===========
+This document describes how to install the Invensense device driver into a
+Linux kernel. The Invensense driver currently supports the following sensors:
+
+- ITG-3500
+- MPU-6050
+- MPU-9150
+- MPU-6500
+- MPU-3050
+
+The slave address of each device is either 0x68 or 0x69, depending on the AD0 pin
+value of the device. Please refer to the appropriate product specification document 
+for further information regarding the AD0 pin. The driver supports both addresses.
+
+The following files are included in this package:
+- Kconfig
+- Makefile
+- inv_mpu_core.c
+- inv_mpu_misc.c
+- inv_mpu_trigger.c
+- inv_mpu3050_iio.c
+- inv_mpu_iio.h
+- inv_mpu_ring.c
+- inv_slave_bma250.c
+- dmpDefaultMPU6050.c
+- dmpkey.h
+- dmpmap.h
+- mpu.h
+
+
+Integrating the Driver in the Linux Kernel
+==========================================
+Please add the files as follows:
+- Add mpu.h to "kernel/include/linux".
+- Add all other files to drivers/staging/iio/imu/mpu
+(another directory is acceptable, but this is the recommended destination)
+
+In order to see the driver in menuconfig when building the kernel, please
+make modifications as shown below:
+
+	modify "drivers/staging/iio/imu/Kconfig" like
+	source "drivers/staging/iio/imu/mpu/Kconfig"
+
+	modify "drivers/staging/iio/imu/Makefile"
+	obj-y += mpu/
+
+
+Board and Platform Data
+=======================
+In order to recognize the Invensense device on the I2C bus, the board file must be modified.
+The i2c_board_info instance must be defined as shown below. 
+
+Interrupt Pin
+-------------
+The hardcoded value of 140 corresponds to the GPIO input pin connected to the Invensense device's interrupt pin.
+This pin will most likely be different for your platform, and the value should be changed accordingly.
+
+Platform Data
+-------------
+The platform data (orientation matrix and secondary bus configurations) must be modified as show below, according
+to your particular platform configuration. 
+
+Please note that the MPU-9150 it is treated as a MPU-6050 with AKM8975 on the device's secondary I2C interface. Thus the secondary I2C address must be provided.
+
+
+Board File Modifications for Secondary I2C Configuration
+========================================================
+For the Panda Board, the board file can be found at arch/arm/mach-omap2/board-omap4panda.c.
+Please modify the pertinent baord file in your system according to the examples shown below:
+
+MPU-6050 + AKM8963 on the secondary I2C interface
+-------------------------------------------------
+static struct mpu_platform_data gyro_platform_data = {
+	.int_config  = 0x10,
+	.level_shifter = 0,
+	.orientation = {  -1,  0,  0,
+			   0,  1,  0,
+			   0,  0, -1 },
+	.sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+	.sec_slave_id   = COMPASS_ID_AK8963,
+	.secondary_i2c_addr = 0x0E
+};
+
+MPU-6500 + AKM8963 on the secondary I2C interface
+-------------------------------------------------
+static struct mpu_platform_data gyro_platform_data = {
+        .int_config  = 0x10,
+        .level_shifter = 0,
+        .orientation = {  -1,  0,  0,
+                           0,  1,  0,
+                           0,  0, -1 },
+        .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+        .sec_slave_id   = COMPASS_ID_AK8963,
+        .secondary_i2c_addr = 0x0E
+};
+
+MPU-9150
+--------
+For MPU-9150, please provide the following secondary I2C bus information.
+
+static struct mpu_platform_data gyro_platform_data = {
+	.int_config  = 0x10,
+	.level_shifter = 0,
+	.orientation = {  -1,  0,  0,
+			   0,  1,  0,
+			   0,  0, -1 },
+	.sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+	.sec_slave_id   = COMPASS_ID_AK8975,
+	.secondary_i2c_addr = 0x0E
+};
+
+MPU-3050 + BMA250 on the secondary I2C interface
+------------------------------------------------
+For BMA250 on the secondary I2C bus, please provide the following information.
+
+static struct mpu_platform_data gyro_platform_data = {
+	.int_config  = 0x10,
+	.level_shifter = 0,
+	.orientation = {  -1,  0,  0,
+			   0,  1,  0,
+			   0,  0, -1 },
+	.sec_slave_type = SECONDARY_SLAVE_TYPE_ACCEL,
+	.sec_slave_id   = ACCEL_ID_BMA250,
+	.secondary_i2c_addr = 0x18,
+};
+
+
+Board File Modifications for Invensense Devices
+===============================================
+For Invensense devices, please provide the i2c init data as shown in the examples below.
+
+In the _i2c_init function, the device is registered in the following manner:
+
+	arch/arm/mach-omap2/board-omap4panda.c
+	    in static int __init omap4_panda_i2c_init(void)
+	omap_register_i2c_bus(4, 400, single_chip_board_info, ARRAY_SIZE(single_chip_board_info));
+
+MPU-3050
+--------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+	{
+		I2C_BOARD_INFO("mpu3050", 0x68),
+		.irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+		.platform_data = &gyro_platform_data,
+	},
+};
+
+ITG-3050
+--------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+	{
+		I2C_BOARD_INFO("itg3500", 0x68),
+		.irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+                .platform_data = &gyro_platform_data,
+	},
+};
+
+MPU6050
+-------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+	{
+		I2C_BOARD_INFO("mpu6050", 0x68),
+		.irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+		.platform_data = &gyro_platform_data,
+	},
+};
+
+MPU6500
+-------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+        {
+                I2C_BOARD_INFO("mpu6500", 0x68),
+                .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+                .platform_data = &gyro_platform_data,
+        },
+};
+
+MPU9150
+-------
+arch/arm/mach-omap2/board-omap4panda.c
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+	{
+		I2C_BOARD_INFO("mpu9150", 0x68),
+		.irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+		.platform_data = &gyro_platform_data,
+	},
+};
+
+
+IIO subsystem
+=============
+A successful installation will create the following two new directories under /sys/bus/iio/devices: 
+	
+	- iio:device0
+	- trigger0
+
+Also, a new file, "iio:device0", will be created in the /dev/ diretory.
+(if you have more than one IIO device, the file will be named "iio:deviceX", where X is a number)
+
+
+Communicating with the Driver in Userspace
+------------------------------------------
+The driver generates several files in sysfs upon installation. 
+These files are used to communicate with the driver. The files can be found 
+at /sys/bus/iio/devices/iio:device0 (or ../iio:deviceX as shown above).
+
+A brief description of the pertinent files for each Invensense device is shown below:
+
+ITG-3500
+--------
+temperature (Read-only)
+--Read temperature data directly from the temperature register.
+
+sampling_frequency (Read/write)
+--Configure the ADC sampling rate and FIFO output rate.
+
+sampling_frequency_available(read-only)
+--show commonly used frequency
+
+clock_source (Read-only)
+--Check which clock-source is used by the chip.
+
+power_state (Read/write)
+--turn on/off the power supply
+
+self_test (read-only)
+--read this entry trigger self test. The return value is D.
+D is the success/fail.
+For different chip, the result is different for success/fail.
+1 means success 0 means fail. The LSB of D is for gyro; the bit
+next to LSB of D is for accel. The bit 2 of D is for compass result.
+
+key (read-only)
+--show the key value of this driver. Used by MPL.
+
+gyro_matrix (read-only)
+--show the orientation matrix obtained from the board file.
+
+MPU-6050 and MPU-6500
+---------------------
+MPU-6050 and MPU-6500 have all sysfs files belonging to ITG-3500 (shown above). 
+In addition, it has the files below:
+
+gyro_enable (read/write)
+--enable/disable gyro functionality. Affects raw_gyro. Turning this off this will
+shut down gyro and save power.
+
+accl_enable (read/write)
+--enable/disable accelerometer functionality. Affects raw_accl.
+Turning this off this will shut down accel and save power.
+
+firmware_loaded (read/write)
+--Flag indicating the whether firmware is loaded or not in the DMP engine.
+0 means no firmware loaded. 1 means firmware is already loaded . This
+flag can only be written as 0. It internally updates to 1.
+
+dmp_on(read/write)
+--This entry controls whether to run DMP or not. 
+Write 1 to enable DMP and write 0 to disable dmp.
+Please note that firmware_loaded must be 1 in order to enable DMP.
+
+dmp_int_on(read/write)
+--This entry controls whether dmp interrupt is on/off. 
+Please note that firmware_loaded must be 1.
+Also, we'd like to remind you that it is sometimes advantageous to 
+turn interrupts off while the DMP is running.
+
+dmp_output_rate
+--control dmp output rate when dmp is on.
+
+dmp_event_int_on(read/write)
+--This entry controls whether dmp event interrupt is on/off. 
+Please note that turning this on will turn off the data interrupt. 
+Interrupts will be generated only when events occur. 
+
+This is useful for saving power when the system is waiting for a special event to wake up.
+
+dmp_firmware (write only binary file)
+--DMP firmware code is loaded into this entry. 
+If loading is successful, the firmware_loaded flag will be updated to 1. 
+In order to load new firmware, the firmware_loaded flag must be first set to 0.
+
+lpa_mode(read-write)
+--Low power accelerometer mode
+
+lpa_freq(read-write)
+--Low power acceleromter frequency.
+
+accel_matrix
+--orientation matrix for accelerometer.
+
+flick_lower,
+flick_upper,
+flick_counter,
+flick_message_on,
+flick_int_on,
+flick_axis,
+--Flick related entries
+
+pedometer_time
+pedometer_steps,
+--Pedometer related entries
+
+event_flick
+event_tap
+event_orientation
+event_display_orientation
+--Event related entries. 
+Please poll these entries to read their values. Direct reads will yield meaningless results.
+Further details are provided in the DMP Events section of this README.
+
+tap_on
+--Controls tap function of DMP
+
+tap_time
+tap_min_count
+tap_threshold
+--Tap related entries. Controls various parameters of tap function.
+
+orientation_on
+--Turn on/off orientation function of DMP.
+
+display_orientation_on
+--Turn on/off display orientation function of DMP.
+
+quaternion_on
+--Turn on/off quaterniion data output. DMP is required for this feature.
+
+MPU-9150
+--------
+MPU-9150 has all of MPU-6050's entries. It also has two additional entries, described below.
+
+compass_enable (read/write)
+--Enables compass function.
+
+compass_matrix (read-only)
+--Compass orientation matrix
+
+MPU-3050 with BMA250 on secondary I2C interface
+-----------------------------------------------
+MPU-3050 with BMA250 on the secondary I2C interface has ever ITG-3500 entry. 
+It also has two additional entries, shown below:
+
+accl_matrix
+
+accl_enable
+
+
+Low Power Accelerometer Mode
+============================
+Lower power accelerometer mode is a special mode that is only available
+in MPU-6050, MPU-6500, and MPU-9150. 
+
+Only accelerometer is functional in this mode.
+"gyro_enable" and "compass_enable" must be zero. "dmp_on" must be zero.
+
+Low power acceleromter mode has two entries: lpa_mode and lpa_freq
+
+To run low power accel mode, set lpa_mode to 1. 
+
+For MPU6050/MPU9150:
+Set lpa_freq to 0~3, which correspond to 1.25Hz, 5Hz, 20Hz, 40Hz. 
+
+For MPU6500:
+Set lpa_freq to 0~11, which correspond to 0.3125Hz to 640Hz.
+
+
+DMP Event
+=========
+A DMP Event is an event that is output by the DMP unit within the Invensense device (MPU).
+Only the MPU-6050, MPU-6500, and MPU-9150 feature the DMP.
+
+
+There are four sysfs entries for DMP events:
+
+- event_flick
+- event_tap
+- event_orientation
+- event_display_orientation
+
+These four events must be polled before reading. 
+
+The proper method to poll sysfs is as follows:
+1. open file.
+2. dummy read.
+3. poll.
+4. once the poll passed, use fopen and fread to read the sysfs entry.
+5. interpret the data.
+
+
+Streaming Data to an Userspace Application
+==========================================
+When streaming data to an userspace application, we recommend that you access
+gyro/accel/compass data via /dev/iio:device0.
+
+Please follow the steps below to read data at a constant rate from the driver:
+
+1. Write a 1 to power_state to turn on the chip. This is the default setting
+   after installing the driver.
+2. Write the desired output rate to fifo_rate.
+3. Write 1 to enable to turn on the event.
+4. Read /dev/iio:device0 to get a string of gyro/accel/compass data.
+5. Parse this string to obtain each gyro/accel/compass element.
+6. If dmp firmware code is loaded, use "dmp_on" to enable/disable dmp.
+7. If compass is enabled, the output will contain compass data.
+
+
+Recommended Sysfs Entry Setup Senquence
+=======================================
+
+Without DMP Firmware
+--------------------
+1. Set "power_state" to 1,
+2. Set the scale and fifo rate values according to your needs.
+3. Set gyro_enable, accel_enable, and compass_enable according to your needs. For example:
+- If you only want gyro data, set accel_enable to 0 (and compass_enable to 0, if applicable).
+- If you only want accel data, set gyro_enable to 0 (and compass_enable to 0, if applicable).
+- If you only want compass data, set gyro_enable to 0 and accel_enable to 0.
+4. Set "enable" to 1.
+5. You will now get the output that you want.
+
+With DMP Firmware
+-----------------
+1. Set "power_state" to 1.
+2. Write "0" to firmware_loaded if it is not zero already.
+3. Load firmware into "dmp_firmware" as a whole. Don't split the DMP firmware image.
+4. Make sure firmware_loaded is 1 after loading the DMP image.
+5. Make appropriate configurations as shown above in the "without DMP firmware" case.
+6. Set dmp_on to 1.
+7. Set "enable" to 1.
+
+Please note that the enable function uses the enable entry under 
+"/sys/bus/iio/devices/iio:device0/buffer"
+
+Test Applications
+=================
+Test applications are located under ARTHROPOD/trunk/software/simple_apps/mpu_iio
+
+Running Test Applications with MPU-9150/MPU-6050/MPU-6500
+---------------------------------------------------------
+To run test applications with MPU-9150, MPU-6050, or MPU-6500 devices, 
+please use the following commands:
+
+1. For orientation/tap/flick/display orientation events:
+
+	mpu_iio  -c 10 -l 3 -p
+
+2. For printing data normally:
+
+	mpu_iio  -c 10 -l 3 -r
+
+Running Test Applications with MPU-3050/ITG-3500
+------------------------------------------------
+To run test applications with MPU-3050 or ITG-3500 devices, 
+please use the following command:
+
+1. For printing data normally: 
+	mpu_iio -c 10 -l 3 -r
+
+Please use mpu_iio.c and iio_utils.h as example code for your development purposes.
\ No newline at end of file
diff --git a/drivers/staging/iio/imu/mpu/dmpDefaultMPU6050.c b/drivers/staging/iio/imu/mpu/dmpDefaultMPU6050.c
new file mode 100644
index 0000000..1620778
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu/dmpDefaultMPU6050.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    dmpDefaultMPU6050.c
+ *      @brief   dmp Default data
+ *      @details This file is part of invensense mpu driver code
+ *
+ */
+
+#include "dmpKey.h"
+#include "dmpmap.h"
+
+#define CFG_27                  (2745)
+#define CFG_20                  (2078)
+#define CFG_23                  (2748)
+#define CFG_FIFO_ON_EVENT       (2694)
+#define CFG_ORIENT_IRQ_1        (2533)
+#define CGNOTICE_INTR           (2636)
+#define X_GRT_Y_TMP             (1318)
+#define CFG_DR_INT              (1029)
+#define CFG_AUTH                (1035)
+#define FCFG_1                  (1062)
+#define SKIP_X_GRT_Y_TMP        (1319)
+#define SKIP_END_COMPARE        (1395)
+#define FCFG_3                  (1110)
+#define FCFG_2                  (1066)
+#define END_COMPARE_Y_X_TMP2    (1415)
+#define CFG_DISPLAY_ORIENT_INT  (1706)
+#define FCFG_7                  (1076)
+#define FCFG_6                  (1128)
+#define NO_ORIENT_INTERRUPT     (1725)
+#define CFG_8                   (2723)
+#define CFG_15                  (2731)
+#define CFG_16                  (2749)
+#define END_COMPARE_Y_X_TMP     (1367)
+#define CFG_6                   (2756)
+#define END_ORIENT_1            (1709)
+#define END_COMPARE_Y_X         (1444)
+#define CFG_LP_QUAT             (2717)
+#define END_ORIENT              (1738)
+#define CFG_FLICK_IN            (2589)
+#define CFG_7                   (1221)
+#define CFG_MOTION_BIAS         (1224)
+#define X_GRT_Y                 (1368)
+#define TEMPLABEL               (2178)
+#define NOT_TIME_MINUS_1        (1528)
+#define END_COMPARE_Y_X_TMP3    (1394)
+#define X_GRT_Y_TMP2            (1339)
+
+#define D_0_22                  (22+512)
+#define D_0_24                  (24+512)
+
+#define D_0_36                  (36)
+#define D_0_52                  (52)
+#define D_0_96                  (96)
+#define D_0_104                 (104)
+#define D_0_108                 (108)
+#define D_0_163                 (163)
+#define D_0_188                 (188)
+#define D_0_192                 (192)
+#define D_0_224                 (224)
+#define D_0_228                 (228)
+#define D_0_232                 (232)
+#define D_0_236                 (236)
+
+#define D_1_2                   (256 + 2)
+#define D_1_4                   (256 + 4)
+#define D_1_8                   (256 + 8)
+#define D_1_10                  (256 + 10)
+#define D_1_24                  (256 + 24)
+#define D_1_28                  (256 + 28)
+#define D_1_36                  (256 + 36)
+#define D_1_40                  (256 + 40)
+#define D_1_44                  (256 + 44)
+#define D_1_72                  (256 + 72)
+#define D_1_74                  (256 + 74)
+#define D_1_79                  (256 + 79)
+#define D_1_88                  (256 + 88)
+#define D_1_90                  (256 + 90)
+#define D_1_92                  (256 + 92)
+#define D_1_96                  (256 + 96)
+#define D_1_98                  (256 + 98)
+#define D_1_106                 (256 + 106)
+#define D_1_108                 (256 + 108)
+#define D_1_112                 (256 + 112)
+#define D_1_128                 (256 + 144)
+#define D_1_152                 (256 + 12)
+#define D_1_160                 (256 + 160)
+#define D_1_176                 (256 + 176)
+#define D_1_178                 (256 + 178)
+#define D_1_218                 (256 + 218)
+#define D_1_232                 (256 + 232)
+#define D_1_236                 (256 + 236)
+#define D_1_240                 (256 + 240)
+#define D_1_244                 (256 + 244)
+#define D_1_250                 (256 + 250)
+#define D_1_252                 (256 + 252)
+#define D_2_12                  (512 + 12)
+#define D_2_96                  (512 + 96)
+#define D_2_108                 (512 + 108)
+#define D_2_208                 (512 + 208)
+#define D_2_224                 (512 + 224)
+#define D_2_236                 (512 + 236)
+#define D_2_244                 (512 + 244)
+#define D_2_248                 (512 + 248)
+#define D_2_252                 (512 + 252)
+
+#define CPASS_BIAS_X            (35 * 16 + 4)
+#define CPASS_BIAS_Y            (35 * 16 + 8)
+#define CPASS_BIAS_Z            (35 * 16 + 12)
+#define CPASS_MTX_00            (36 * 16)
+#define CPASS_MTX_01            (36 * 16 + 4)
+#define CPASS_MTX_02            (36 * 16 + 8)
+#define CPASS_MTX_10            (36 * 16 + 12)
+#define CPASS_MTX_11            (37 * 16)
+#define CPASS_MTX_12            (37 * 16 + 4)
+#define CPASS_MTX_20            (37 * 16 + 8)
+#define CPASS_MTX_21            (37 * 16 + 12)
+#define CPASS_MTX_22            (43 * 16 + 12)
+#define D_ACT0                  (40 * 16)
+#define D_ACSX                  (40 * 16 + 4)
+#define D_ACSY                  (40 * 16 + 8)
+#define D_ACSZ                  (40 * 16 + 12)
+
+#define FLICK_MSG               (45 * 16 + 4)
+#define FLICK_COUNTER           (45 * 16 + 8)
+#define FLICK_LOWER             (45 * 16 + 12)
+#define FLICK_UPPER             (46 * 16 + 12)
+
+#define D_AUTH_OUT               (992)
+#define D_AUTH_IN                (996)
+#define D_AUTH_A                 (1000)
+#define D_AUTH_B                 (1004)
+
+#define D_PEDSTD_BP_B          (768 + 0x1C)
+#define D_PEDSTD_HP_A          (768 + 0x78)
+#define D_PEDSTD_HP_B          (768 + 0x7C)
+#define D_PEDSTD_BP_A4         (768 + 0x40)
+#define D_PEDSTD_BP_A3         (768 + 0x44)
+#define D_PEDSTD_BP_A2         (768 + 0x48)
+#define D_PEDSTD_BP_A1         (768 + 0x4C)
+#define D_PEDSTD_INT_THRSH     (768 + 0x68)
+#define D_PEDSTD_CLIP          (768 + 0x6C)
+#define D_PEDSTD_SB            (768 + 0x28)
+#define D_PEDSTD_SB_TIME       (768 + 0x2C)
+#define D_PEDSTD_PEAKTHRSH     (768 + 0x98)
+#define D_PEDSTD_TIML          (768 + 0x2A)
+#define D_PEDSTD_TIMH          (768 + 0x2E)
+#define D_PEDSTD_PEAK          (768 + 0X94)
+#define D_PEDSTD_STEPCTR       (768 + 0x60)
+#define D_PEDSTD_TIMECTR       (964)
+#define D_PEDSTD_DECI          (768 + 0xA0)
+
+#define D_HOST_NO_MOT          (976)
+
+static const struct tKeyLabel dmpTConfig[] = {
+	{KEY_CFG_27,                    CFG_27},
+	{KEY_CFG_20,                    CFG_20},
+	{KEY_CFG_23,                    CFG_23},
+	{KEY_CFG_FIFO_ON_EVENT,         CFG_FIFO_ON_EVENT},
+	{KEY_CFG_ORIENT_IRQ_1,          CFG_ORIENT_IRQ_1},
+	{KEY_CGNOTICE_INTR,             CGNOTICE_INTR},
+	{KEY_X_GRT_Y_TMP,               X_GRT_Y_TMP},
+	{KEY_CFG_DR_INT,                CFG_DR_INT},
+	{KEY_CFG_AUTH,                  CFG_AUTH},
+	{KEY_FCFG_1,                    FCFG_1},
+	{KEY_SKIP_X_GRT_Y_TMP,          SKIP_X_GRT_Y_TMP},
+	{KEY_SKIP_END_COMPARE,          SKIP_END_COMPARE},
+	{KEY_FCFG_3,                    FCFG_3},
+	{KEY_FCFG_2,                    FCFG_2},
+	{KEY_END_COMPARE_Y_X_TMP2,      END_COMPARE_Y_X_TMP2},
+	{KEY_CFG_DISPLAY_ORIENT_INT,    CFG_DISPLAY_ORIENT_INT},
+	{KEY_FCFG_7,                    FCFG_7},
+	{KEY_FCFG_6,                    FCFG_6},
+	{KEY_NO_ORIENT_INTERRUPT,       NO_ORIENT_INTERRUPT},
+	{KEY_CFG_8,                     CFG_8},
+	{KEY_CFG_15,                    CFG_15},
+	{KEY_CFG_16,                    CFG_16},
+	{KEY_END_COMPARE_Y_X_TMP,       END_COMPARE_Y_X_TMP},
+	{KEY_CFG_6,                     CFG_6},
+	{KEY_END_ORIENT_1,              END_ORIENT_1},
+	{KEY_END_COMPARE_Y_X,           END_COMPARE_Y_X},
+	{KEY_CFG_LP_QUAT,               CFG_LP_QUAT},
+	{KEY_END_ORIENT,                END_ORIENT},
+	{KEY_CFG_FLICK_IN,              CFG_FLICK_IN},
+	{KEY_CFG_7,                     CFG_7},
+	{KEY_CFG_MOTION_BIAS,           CFG_MOTION_BIAS},
+	{KEY_X_GRT_Y,                   X_GRT_Y},
+	{KEY_TEMPLABEL,                 TEMPLABEL},
+	{KEY_NOT_TIME_MINUS_1,          NOT_TIME_MINUS_1},
+	{KEY_END_COMPARE_Y_X_TMP3,      END_COMPARE_Y_X_TMP3},
+	{KEY_X_GRT_Y_TMP2,              X_GRT_Y_TMP2},
+	{KEY_D_0_22,                D_0_22},
+	{KEY_D_0_96,                D_0_96},
+	{KEY_D_0_104,               D_0_104},
+	{KEY_D_0_108,               D_0_108},
+	{KEY_D_1_36,               D_1_36},
+	{KEY_D_1_40,               D_1_40},
+	{KEY_D_1_44,               D_1_44},
+	{KEY_D_1_72,               D_1_72},
+	{KEY_D_1_74,               D_1_74},
+	{KEY_D_1_79,               D_1_79},
+	{KEY_D_1_88,               D_1_88},
+	{KEY_D_1_90,               D_1_90},
+	{KEY_D_1_92,               D_1_92},
+	{KEY_D_1_160,               D_1_160},
+	{KEY_D_1_176,               D_1_176},
+	{KEY_D_1_218,               D_1_218},
+	{KEY_D_1_232,               D_1_232},
+	{KEY_D_1_250,               D_1_250},
+	{KEY_DMP_TAPW_MIN,          DMP_TAPW_MIN},
+	{KEY_DMP_TAP_THR_X,         DMP_TAP_THX},
+	{KEY_DMP_TAP_THR_Y,         DMP_TAP_THY},
+	{KEY_DMP_TAP_THR_Z,         DMP_TAP_THZ},
+	{KEY_DMP_SH_TH_Y,           DMP_SH_TH_Y},
+	{KEY_DMP_SH_TH_X,           DMP_SH_TH_X},
+	{KEY_DMP_SH_TH_Z,           DMP_SH_TH_Z},
+	{KEY_DMP_ORIENT,            DMP_ORIENT},
+	{KEY_D_AUTH_OUT,            D_AUTH_OUT},
+	{KEY_D_AUTH_IN,             D_AUTH_IN},
+	{KEY_D_AUTH_A,              D_AUTH_A},
+	{KEY_D_AUTH_B,              D_AUTH_B},
+	{KEY_CPASS_BIAS_X,          CPASS_BIAS_X},
+	{KEY_CPASS_BIAS_Y,          CPASS_BIAS_Y},
+	{KEY_CPASS_BIAS_Z,          CPASS_BIAS_Z},
+	{KEY_CPASS_MTX_00,          CPASS_MTX_00},
+	{KEY_CPASS_MTX_01,          CPASS_MTX_01},
+	{KEY_CPASS_MTX_02,          CPASS_MTX_02},
+	{KEY_CPASS_MTX_10,          CPASS_MTX_10},
+	{KEY_CPASS_MTX_11,          CPASS_MTX_11},
+	{KEY_CPASS_MTX_12,          CPASS_MTX_12},
+	{KEY_CPASS_MTX_20,          CPASS_MTX_20},
+	{KEY_CPASS_MTX_21,          CPASS_MTX_21},
+	{KEY_CPASS_MTX_22,          CPASS_MTX_22},
+	{KEY_D_ACT0,                D_ACT0},
+	{KEY_D_ACSX,                D_ACSX},
+	{KEY_D_ACSY,                D_ACSY},
+	{KEY_D_ACSZ,                D_ACSZ},
+	{KEY_FLICK_MSG,             FLICK_MSG},
+	{KEY_FLICK_COUNTER,         FLICK_COUNTER},
+	{KEY_FLICK_LOWER,           FLICK_LOWER},
+	{KEY_FLICK_UPPER,           FLICK_UPPER},
+	{KEY_D_PEDSTD_BP_B, D_PEDSTD_BP_B},
+	{KEY_D_PEDSTD_HP_A, D_PEDSTD_HP_A},
+	{KEY_D_PEDSTD_HP_B, D_PEDSTD_HP_B},
+	{KEY_D_PEDSTD_BP_A4, D_PEDSTD_BP_A4},
+	{KEY_D_PEDSTD_BP_A3, D_PEDSTD_BP_A3},
+	{KEY_D_PEDSTD_BP_A2, D_PEDSTD_BP_A2},
+	{KEY_D_PEDSTD_BP_A1, D_PEDSTD_BP_A1},
+	{KEY_D_PEDSTD_INT_THRSH, D_PEDSTD_INT_THRSH},
+	{KEY_D_PEDSTD_CLIP, D_PEDSTD_CLIP},
+	{KEY_D_PEDSTD_SB, D_PEDSTD_SB},
+	{KEY_D_PEDSTD_SB_TIME, D_PEDSTD_SB_TIME},
+	{KEY_D_PEDSTD_PEAKTHRSH, D_PEDSTD_PEAKTHRSH},
+	{KEY_D_PEDSTD_TIML,      D_PEDSTD_TIML},
+	{KEY_D_PEDSTD_TIMH,      D_PEDSTD_TIMH},
+	{KEY_D_PEDSTD_PEAK,      D_PEDSTD_PEAK},
+	{KEY_D_PEDSTD_STEPCTR,   D_PEDSTD_STEPCTR},
+	{KEY_D_PEDSTD_TIMECTR,  D_PEDSTD_TIMECTR},
+	{KEY_D_PEDSTD_DECI,  D_PEDSTD_DECI},
+	{KEY_D_HOST_NO_MOT,  D_HOST_NO_MOT}
+};
+#define NUM_LOCAL_KEYS (sizeof(dmpTConfig)/sizeof(dmpTConfig[0]))
+
+static struct tKeyLabel keys[NUM_KEYS];
+
+unsigned short inv_dmp_get_address(unsigned short key)
+{
+	static int isSorted;
+	if (!isSorted) {
+		int kk;
+		for (kk = 0; kk < NUM_KEYS; ++kk) {
+			keys[kk].addr = 0xffff;
+			keys[kk].key = kk;
+		}
+		for (kk = 0; kk < NUM_LOCAL_KEYS; ++kk)
+			keys[dmpTConfig[kk].key].addr = dmpTConfig[kk].addr;
+		isSorted = 1;
+	}
+	if (key >= NUM_KEYS)
+		return 0xffff;
+	return keys[key].addr;
+}
+/**
+ *  @}
+ */
diff --git a/drivers/staging/iio/imu/mpu/dmpKey.h b/drivers/staging/iio/imu/mpu/dmpKey.h
new file mode 100644
index 0000000..d19b15a
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu/dmpKey.h
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    dmpKey.h
+ *      @brief   dmp Key definition
+ *      @details This file is part of invensense mpu driver code
+ *
+ */
+
+#ifndef DMPKEY_H__
+#define DMPKEY_H__
+
+#define KEY_CFG_25                  (0)
+#define KEY_CFG_24                  (KEY_CFG_25 + 1)
+#define KEY_CFG_26                  (KEY_CFG_24 + 1)
+#define KEY_CFG_27                  (KEY_CFG_26 + 1)
+#define KEY_CFG_21                  (KEY_CFG_27 + 1)
+#define KEY_CFG_20                  (KEY_CFG_21 + 1)
+#define KEY_CFG_TAP4                (KEY_CFG_20 + 1)
+#define KEY_CFG_TAP5                (KEY_CFG_TAP4 + 1)
+#define KEY_CFG_TAP6                (KEY_CFG_TAP5 + 1)
+#define KEY_CFG_TAP7                (KEY_CFG_TAP6 + 1)
+#define KEY_CFG_TAP0                (KEY_CFG_TAP7 + 1)
+#define KEY_CFG_TAP1                (KEY_CFG_TAP0 + 1)
+#define KEY_CFG_TAP2                (KEY_CFG_TAP1 + 1)
+#define KEY_CFG_TAP3                (KEY_CFG_TAP2 + 1)
+#define KEY_CFG_TAP_QUANTIZE        (KEY_CFG_TAP3 + 1)
+#define KEY_CFG_TAP_JERK            (KEY_CFG_TAP_QUANTIZE + 1)
+#define KEY_CFG_DR_INT              (KEY_CFG_TAP_JERK + 1)
+#define KEY_CFG_AUTH                (KEY_CFG_DR_INT + 1)
+#define KEY_CFG_TAP_SAVE_ACCB       (KEY_CFG_AUTH + 1)
+#define KEY_CFG_TAP_CLEAR_STICKY    (KEY_CFG_TAP_SAVE_ACCB + 1)
+#define KEY_CFG_FIFO_ON_EVENT       (KEY_CFG_TAP_CLEAR_STICKY + 1)
+#define KEY_FCFG_ACCEL_INPUT        (KEY_CFG_FIFO_ON_EVENT + 1)
+#define KEY_FCFG_ACCEL_INIT         (KEY_FCFG_ACCEL_INPUT + 1)
+#define KEY_CFG_23                  (KEY_FCFG_ACCEL_INIT + 1)
+#define KEY_FCFG_1                  (KEY_CFG_23 + 1)
+#define KEY_FCFG_3                  (KEY_FCFG_1 + 1)
+#define KEY_FCFG_2                  (KEY_FCFG_3 + 1)
+#define KEY_CFG_3D                  (KEY_FCFG_2 + 1)
+#define KEY_CFG_3B                  (KEY_CFG_3D + 1)
+#define KEY_CFG_3C                  (KEY_CFG_3B + 1)
+#define KEY_FCFG_5                  (KEY_CFG_3C + 1)
+#define KEY_FCFG_4                  (KEY_FCFG_5 + 1)
+#define KEY_FCFG_7                  (KEY_FCFG_4 + 1)
+#define KEY_FCFG_FSCALE             (KEY_FCFG_7 + 1)
+#define KEY_FCFG_AZ                 (KEY_FCFG_FSCALE + 1)
+#define KEY_FCFG_6                  (KEY_FCFG_AZ + 1)
+#define KEY_FCFG_LSB4               (KEY_FCFG_6 + 1)
+#define KEY_CFG_12                  (KEY_FCFG_LSB4 + 1)
+#define KEY_CFG_14                  (KEY_CFG_12 + 1)
+#define KEY_CFG_15                  (KEY_CFG_14 + 1)
+#define KEY_CFG_16                  (KEY_CFG_15 + 1)
+#define KEY_CFG_18                  (KEY_CFG_16 + 1)
+#define KEY_CFG_6                   (KEY_CFG_18 + 1)
+#define KEY_CFG_7                   (KEY_CFG_6 + 1)
+#define KEY_CFG_4                   (KEY_CFG_7 + 1)
+#define KEY_CFG_5                   (KEY_CFG_4 + 1)
+#define KEY_CFG_2                   (KEY_CFG_5 + 1)
+#define KEY_CFG_3                   (KEY_CFG_2 + 1)
+#define KEY_CFG_1                   (KEY_CFG_3 + 1)
+#define KEY_CFG_EXTERNAL            (KEY_CFG_1 + 1)
+#define KEY_CFG_8                   (KEY_CFG_EXTERNAL + 1)
+#define KEY_CFG_9                   (KEY_CFG_8 + 1)
+#define KEY_CFG_ORIENT_3            (KEY_CFG_9 + 1)
+#define KEY_CFG_ORIENT_2            (KEY_CFG_ORIENT_3 + 1)
+#define KEY_CFG_ORIENT_1            (KEY_CFG_ORIENT_2 + 1)
+#define KEY_CFG_GYRO_SOURCE         (KEY_CFG_ORIENT_1 + 1)
+#define KEY_CFG_ORIENT_IRQ_1        (KEY_CFG_GYRO_SOURCE + 1)
+#define KEY_CFG_ORIENT_IRQ_2        (KEY_CFG_ORIENT_IRQ_1 + 1)
+#define KEY_CFG_ORIENT_IRQ_3        (KEY_CFG_ORIENT_IRQ_2 + 1)
+#define KEY_FCFG_MAG_VAL            (KEY_CFG_ORIENT_IRQ_3 + 1)
+#define KEY_FCFG_MAG_MOV            (KEY_FCFG_MAG_VAL + 1)
+#define KEY_CFG_LP_QUAT             (KEY_FCFG_MAG_MOV + 1)
+
+/* MPU6050 keys */
+#define KEY_CFG_ACCEL_FILTER        (KEY_CFG_LP_QUAT + 1)
+#define KEY_CFG_MOTION_BIAS         (KEY_CFG_ACCEL_FILTER + 1)
+#define KEY_TEMPLABEL               (KEY_CFG_MOTION_BIAS + 1)
+
+#define KEY_D_0_22                  (KEY_TEMPLABEL + 1)
+#define KEY_D_0_24                  (KEY_D_0_22 + 1)
+#define KEY_D_0_36                  (KEY_D_0_24 + 1)
+#define KEY_D_0_52                  (KEY_D_0_36 + 1)
+#define KEY_D_0_96                  (KEY_D_0_52 + 1)
+#define KEY_D_0_104                 (KEY_D_0_96 + 1)
+#define KEY_D_0_108                 (KEY_D_0_104 + 1)
+#define KEY_D_0_163                 (KEY_D_0_108 + 1)
+#define KEY_D_0_188                 (KEY_D_0_163 + 1)
+#define KEY_D_0_192                 (KEY_D_0_188 + 1)
+#define KEY_D_0_224                 (KEY_D_0_192 + 1)
+#define KEY_D_0_228                 (KEY_D_0_224 + 1)
+#define KEY_D_0_232                 (KEY_D_0_228 + 1)
+#define KEY_D_0_236                 (KEY_D_0_232 + 1)
+
+#define KEY_DMP_PREVPTAT            (KEY_D_0_236 + 1)
+#define KEY_D_1_2                   (KEY_DMP_PREVPTAT + 1)
+#define KEY_D_1_4                   (KEY_D_1_2 + 1)
+#define KEY_D_1_8                   (KEY_D_1_4 + 1)
+#define KEY_D_1_10                  (KEY_D_1_8 + 1)
+#define KEY_D_1_24                  (KEY_D_1_10 + 1)
+#define KEY_D_1_28                  (KEY_D_1_24 + 1)
+#define KEY_D_1_36                  (KEY_D_1_28 + 1)
+#define KEY_D_1_40                  (KEY_D_1_36 + 1)
+#define KEY_D_1_44                  (KEY_D_1_40 + 1)
+#define KEY_D_1_72                  (KEY_D_1_44 + 1)
+#define KEY_D_1_74                  (KEY_D_1_72 + 1)
+#define KEY_D_1_79                  (KEY_D_1_74 + 1)
+#define KEY_D_1_88                  (KEY_D_1_79 + 1)
+#define KEY_D_1_90                  (KEY_D_1_88 + 1)
+#define KEY_D_1_92                  (KEY_D_1_90 + 1)
+#define KEY_D_1_96                  (KEY_D_1_92 + 1)
+#define KEY_D_1_98                  (KEY_D_1_96 + 1)
+#define KEY_D_1_100                 (KEY_D_1_98 + 1)
+#define KEY_D_1_106                 (KEY_D_1_100 + 1)
+#define KEY_D_1_108                 (KEY_D_1_106 + 1)
+#define KEY_D_1_112                 (KEY_D_1_108 + 1)
+#define KEY_D_1_128                 (KEY_D_1_112 + 1)
+#define KEY_D_1_152                 (KEY_D_1_128 + 1)
+#define KEY_D_1_160                 (KEY_D_1_152 + 1)
+#define KEY_D_1_168                 (KEY_D_1_160 + 1)
+#define KEY_D_1_175                 (KEY_D_1_168 + 1)
+#define KEY_D_1_176                 (KEY_D_1_175 + 1)
+#define KEY_D_1_178                 (KEY_D_1_176 + 1)
+#define KEY_D_1_179                 (KEY_D_1_178 + 1)
+#define KEY_D_1_218                 (KEY_D_1_179 + 1)
+#define KEY_D_1_232                 (KEY_D_1_218 + 1)
+#define KEY_D_1_236                 (KEY_D_1_232 + 1)
+#define KEY_D_1_240                 (KEY_D_1_236 + 1)
+#define KEY_D_1_244                 (KEY_D_1_240 + 1)
+#define KEY_D_1_250                 (KEY_D_1_244 + 1)
+#define KEY_D_1_252                 (KEY_D_1_250 + 1)
+#define KEY_D_2_12                  (KEY_D_1_252 + 1)
+#define KEY_D_2_96                  (KEY_D_2_12 + 1)
+#define KEY_D_2_108                 (KEY_D_2_96 + 1)
+#define KEY_D_2_208                 (KEY_D_2_108 + 1)
+#define KEY_FLICK_MSG               (KEY_D_2_208 + 1)
+#define KEY_FLICK_COUNTER           (KEY_FLICK_MSG + 1)
+#define KEY_FLICK_LOWER             (KEY_FLICK_COUNTER + 1)
+#define KEY_CFG_FLICK_IN            (KEY_FLICK_LOWER + 1)
+#define KEY_FLICK_UPPER             (KEY_CFG_FLICK_IN + 1)
+#define KEY_CGNOTICE_INTR           (KEY_FLICK_UPPER + 1)
+#define KEY_D_2_224                 (KEY_CGNOTICE_INTR + 1)
+#define KEY_D_2_244                 (KEY_D_2_224 + 1)
+#define KEY_D_2_248                 (KEY_D_2_244 + 1)
+#define KEY_D_2_252                 (KEY_D_2_248 + 1)
+
+#define KEY_D_GYRO_BIAS_X               (KEY_D_2_252 + 1)
+#define KEY_D_GYRO_BIAS_Y               (KEY_D_GYRO_BIAS_X + 1)
+#define KEY_D_GYRO_BIAS_Z               (KEY_D_GYRO_BIAS_Y + 1)
+#define KEY_D_GYRO_ENABLE               (KEY_D_GYRO_BIAS_Z + 1)
+#define KEY_D_ACCEL_ENABLE              (KEY_D_GYRO_ENABLE + 1)
+#define KEY_D_QUAT_ENABLE               (KEY_D_ACCEL_ENABLE + 1)
+#define KEY_D_CR_TIME_G                 (KEY_D_QUAT_ENABLE + 1)
+#define KEY_D_CR_TIME_A                 (KEY_D_CR_TIME_G + 1)
+#define KEY_D_CR_TIME_Q                 (KEY_D_CR_TIME_A + 1)
+#define KEY_D_CS_TAX                    (KEY_D_CR_TIME_Q + 1)
+#define KEY_D_CS_TAY                    (KEY_D_CS_TAX + 1)
+#define KEY_D_CS_TAZ                    (KEY_D_CS_TAY + 1)
+
+#define KEY_D_CS_TGX                    (KEY_D_CS_TAZ + 1)
+#define KEY_D_CS_TGY                    (KEY_D_CS_TGX + 1)
+#define KEY_D_CS_TGZ                    (KEY_D_CS_TGY + 1)
+#define KEY_D_CS_TQ0                    (KEY_D_CS_TGZ + 1)
+#define KEY_D_CS_TQ1                    (KEY_D_CS_TQ0 + 1)
+#define KEY_D_CS_TQ2                    (KEY_D_CS_TQ1 + 1)
+#define KEY_D_CS_TQ3                    (KEY_D_CS_TQ2 + 1)
+
+/* Compass keys */
+#define KEY_CPASS_BIAS_X            (KEY_D_CS_TQ3 + 1)
+#define KEY_CPASS_BIAS_Y            (KEY_CPASS_BIAS_X + 1)
+#define KEY_CPASS_BIAS_Z            (KEY_CPASS_BIAS_Y + 1)
+#define KEY_CPASS_MTX_00            (KEY_CPASS_BIAS_Z + 1)
+#define KEY_CPASS_MTX_01            (KEY_CPASS_MTX_00 + 1)
+#define KEY_CPASS_MTX_02            (KEY_CPASS_MTX_01 + 1)
+#define KEY_CPASS_MTX_10            (KEY_CPASS_MTX_02 + 1)
+#define KEY_CPASS_MTX_11            (KEY_CPASS_MTX_10 + 1)
+#define KEY_CPASS_MTX_12            (KEY_CPASS_MTX_11 + 1)
+#define KEY_CPASS_MTX_20            (KEY_CPASS_MTX_12 + 1)
+#define KEY_CPASS_MTX_21            (KEY_CPASS_MTX_20 + 1)
+#define KEY_CPASS_MTX_22            (KEY_CPASS_MTX_21 + 1)
+
+/* Gesture Keys */
+#define KEY_DMP_TAPW_MIN            (KEY_CPASS_MTX_22 + 1)
+#define KEY_DMP_TAP_THR_X           (KEY_DMP_TAPW_MIN + 1)
+#define KEY_DMP_TAP_THR_Y           (KEY_DMP_TAP_THR_X + 1)
+#define KEY_DMP_TAP_THR_Z           (KEY_DMP_TAP_THR_Y + 1)
+#define KEY_DMP_SH_TH_Y             (KEY_DMP_TAP_THR_Z + 1)
+#define KEY_DMP_SH_TH_X             (KEY_DMP_SH_TH_Y + 1)
+#define KEY_DMP_SH_TH_Z             (KEY_DMP_SH_TH_X + 1)
+#define KEY_DMP_ORIENT              (KEY_DMP_SH_TH_Z + 1)
+#define KEY_D_ACT0                  (KEY_DMP_ORIENT + 1)
+#define KEY_D_ACSX                  (KEY_D_ACT0 + 1)
+#define KEY_D_ACSY                  (KEY_D_ACSX + 1)
+#define KEY_D_ACSZ                  (KEY_D_ACSY + 1)
+
+#define KEY_X_GRT_Y_TMP             (KEY_D_ACSZ + 1)
+#define KEY_SKIP_X_GRT_Y_TMP        (KEY_X_GRT_Y_TMP + 1)
+#define KEY_SKIP_END_COMPARE        (KEY_SKIP_X_GRT_Y_TMP + 1)
+#define KEY_END_COMPARE_Y_X_TMP2    (KEY_SKIP_END_COMPARE + 1)
+#define KEY_CFG_DISPLAY_ORIENT_INT  (KEY_END_COMPARE_Y_X_TMP2 + 1)
+#define KEY_NO_ORIENT_INTERRUPT     (KEY_CFG_DISPLAY_ORIENT_INT + 1)
+#define KEY_END_COMPARE_Y_X_TMP     (KEY_NO_ORIENT_INTERRUPT + 1)
+#define KEY_END_ORIENT_1            (KEY_END_COMPARE_Y_X_TMP + 1)
+#define KEY_END_COMPARE_Y_X         (KEY_END_ORIENT_1 + 1)
+#define KEY_END_ORIENT              (KEY_END_COMPARE_Y_X + 1)
+#define KEY_X_GRT_Y                 (KEY_END_ORIENT + 1)
+#define KEY_NOT_TIME_MINUS_1        (KEY_X_GRT_Y + 1)
+#define KEY_END_COMPARE_Y_X_TMP3    (KEY_NOT_TIME_MINUS_1 + 1)
+#define KEY_X_GRT_Y_TMP2            (KEY_END_COMPARE_Y_X_TMP3 + 1)
+
+/* Authenticate Keys */
+#define KEY_D_AUTH_OUT              (KEY_X_GRT_Y_TMP2 + 1)
+#define KEY_D_AUTH_IN               (KEY_D_AUTH_OUT + 1)
+#define KEY_D_AUTH_A                (KEY_D_AUTH_IN + 1)
+#define KEY_D_AUTH_B                (KEY_D_AUTH_A + 1)
+
+/* Pedometer standalone only keys */
+#define KEY_D_PEDSTD_BP_B           (KEY_D_AUTH_B + 1)
+#define KEY_D_PEDSTD_HP_A           (KEY_D_PEDSTD_BP_B + 1)
+#define KEY_D_PEDSTD_HP_B           (KEY_D_PEDSTD_HP_A + 1)
+#define KEY_D_PEDSTD_BP_A4          (KEY_D_PEDSTD_HP_B + 1)
+#define KEY_D_PEDSTD_BP_A3          (KEY_D_PEDSTD_BP_A4 + 1)
+#define KEY_D_PEDSTD_BP_A2          (KEY_D_PEDSTD_BP_A3 + 1)
+#define KEY_D_PEDSTD_BP_A1          (KEY_D_PEDSTD_BP_A2 + 1)
+#define KEY_D_PEDSTD_INT_THRSH      (KEY_D_PEDSTD_BP_A1 + 1)
+#define KEY_D_PEDSTD_CLIP           (KEY_D_PEDSTD_INT_THRSH + 1)
+#define KEY_D_PEDSTD_SB             (KEY_D_PEDSTD_CLIP + 1)
+#define KEY_D_PEDSTD_SB_TIME        (KEY_D_PEDSTD_SB + 1)
+#define KEY_D_PEDSTD_PEAKTHRSH      (KEY_D_PEDSTD_SB_TIME + 1)
+#define KEY_D_PEDSTD_TIML           (KEY_D_PEDSTD_PEAKTHRSH + 1)
+#define KEY_D_PEDSTD_TIMH           (KEY_D_PEDSTD_TIML + 1)
+#define KEY_D_PEDSTD_PEAK           (KEY_D_PEDSTD_TIMH + 1)
+#define KEY_D_PEDSTD_TIMECTR        (KEY_D_PEDSTD_PEAK + 1)
+#define KEY_D_PEDSTD_STEPCTR        (KEY_D_PEDSTD_TIMECTR + 1)
+#define KEY_D_PEDSTD_WALKTIME       (KEY_D_PEDSTD_STEPCTR + 1)
+#define KEY_D_PEDSTD_DECI           (KEY_D_PEDSTD_WALKTIME + 1)
+
+/*Host Based No Motion*/
+#define KEY_D_HOST_NO_MOT           (KEY_D_PEDSTD_DECI + 1)
+
+/* EIS keys */
+#define KEY_P_EIS_FIFO_FOOTER       (KEY_D_HOST_NO_MOT + 1)
+#define KEY_P_EIS_FIFO_YSHIFT       (KEY_P_EIS_FIFO_FOOTER + 1)
+#define KEY_P_EIS_DATA_RATE         (KEY_P_EIS_FIFO_YSHIFT + 1)
+#define KEY_P_EIS_FIFO_XSHIFT       (KEY_P_EIS_DATA_RATE + 1)
+#define KEY_P_EIS_FIFO_SYNC         (KEY_P_EIS_FIFO_XSHIFT + 1)
+#define KEY_P_EIS_FIFO_ZSHIFT       (KEY_P_EIS_FIFO_SYNC + 1)
+#define KEY_P_EIS_FIFO_READY        (KEY_P_EIS_FIFO_ZSHIFT + 1)
+#define KEY_DMP_FOOTER              (KEY_P_EIS_FIFO_READY + 1)
+#define KEY_DMP_INTX_HC             (KEY_DMP_FOOTER + 1)
+#define KEY_DMP_INTX_PH             (KEY_DMP_INTX_HC + 1)
+#define KEY_DMP_INTX_SH             (KEY_DMP_INTX_PH + 1)
+#define KEY_DMP_AINV_SH             (KEY_DMP_INTX_SH + 1)
+#define KEY_DMP_A_INV_XH            (KEY_DMP_AINV_SH + 1)
+#define KEY_DMP_AINV_PH             (KEY_DMP_A_INV_XH + 1)
+#define KEY_DMP_CTHX_H              (KEY_DMP_AINV_PH + 1)
+#define KEY_DMP_CTHY_H              (KEY_DMP_CTHX_H + 1)
+#define KEY_DMP_CTHZ_H              (KEY_DMP_CTHY_H + 1)
+#define KEY_DMP_NCTHX_H             (KEY_DMP_CTHZ_H + 1)
+#define KEY_DMP_NCTHY_H             (KEY_DMP_NCTHX_H + 1)
+#define KEY_DMP_NCTHZ_H             (KEY_DMP_NCTHY_H + 1)
+#define KEY_DMP_CTSQ_XH             (KEY_DMP_NCTHZ_H + 1)
+#define KEY_DMP_CTSQ_YH             (KEY_DMP_CTSQ_XH + 1)
+#define KEY_DMP_CTSQ_ZH             (KEY_DMP_CTSQ_YH + 1)
+#define KEY_DMP_INTX_H              (KEY_DMP_CTSQ_ZH + 1)
+#define KEY_DMP_INTY_H              (KEY_DMP_INTX_H + 1)
+#define KEY_DMP_INTZ_H              (KEY_DMP_INTY_H + 1)
+#define KEY_DMP_HPX_H               (KEY_DMP_INTZ_H + 1)
+#define KEY_DMP_HPY_H               (KEY_DMP_HPX_H + 1)
+#define KEY_DMP_HPZ_H               (KEY_DMP_HPY_H + 1)
+
+/* Stream keys */
+#define KEY_STREAM_P_GYRO_Z         (KEY_DMP_HPZ_H + 1)
+#define KEY_STREAM_P_GYRO_Y         (KEY_STREAM_P_GYRO_Z + 1)
+#define KEY_STREAM_P_GYRO_X         (KEY_STREAM_P_GYRO_Y + 1)
+#define KEY_STREAM_P_TEMP           (KEY_STREAM_P_GYRO_X + 1)
+#define KEY_STREAM_P_AUX_Y          (KEY_STREAM_P_TEMP + 1)
+#define KEY_STREAM_P_AUX_X          (KEY_STREAM_P_AUX_Y + 1)
+#define KEY_STREAM_P_AUX_Z          (KEY_STREAM_P_AUX_X + 1)
+#define KEY_STREAM_P_ACCEL_Y        (KEY_STREAM_P_AUX_Z + 1)
+#define KEY_STREAM_P_ACCEL_X        (KEY_STREAM_P_ACCEL_Y + 1)
+#define KEY_STREAM_P_FOOTER         (KEY_STREAM_P_ACCEL_X + 1)
+#define KEY_STREAM_P_ACCEL_Z        (KEY_STREAM_P_FOOTER + 1)
+
+#define NUM_KEYS                    (KEY_STREAM_P_ACCEL_Z + 1)
+
+struct tKeyLabel  {
+	unsigned short key;
+	unsigned short addr;
+};
+
+#define DINA0A 0x0a
+#define DINA22 0x22
+#define DINA42 0x42
+#define DINA5A 0x5a
+
+#define DINA06 0x06
+#define DINA0E 0x0e
+#define DINA16 0x16
+#define DINA1E 0x1e
+#define DINA26 0x26
+#define DINA2E 0x2e
+#define DINA36 0x36
+#define DINA3E 0x3e
+#define DINA46 0x46
+#define DINA4E 0x4e
+#define DINA56 0x56
+#define DINA5E 0x5e
+#define DINA66 0x66
+#define DINA6E 0x6e
+#define DINA76 0x76
+#define DINA7E 0x7e
+
+#define DINA00 0x00
+#define DINA08 0x08
+#define DINA10 0x10
+#define DINA18 0x18
+#define DINA20 0x20
+#define DINA28 0x28
+#define DINA30 0x30
+#define DINA38 0x38
+#define DINA40 0x40
+#define DINA48 0x48
+#define DINA50 0x50
+#define DINA58 0x58
+#define DINA60 0x60
+#define DINA68 0x68
+#define DINA70 0x70
+#define DINA78 0x78
+
+#define DINA04 0x04
+#define DINA0C 0x0c
+#define DINA14 0x14
+#define DINA1C 0x1C
+#define DINA24 0x24
+#define DINA2C 0x2c
+#define DINA34 0x34
+#define DINA3C 0x3c
+#define DINA44 0x44
+#define DINA4C 0x4c
+#define DINA54 0x54
+#define DINA5C 0x5c
+#define DINA64 0x64
+#define DINA6C 0x6c
+#define DINA74 0x74
+#define DINA7C 0x7c
+
+#define DINA01 0x01
+#define DINA09 0x09
+#define DINA11 0x11
+#define DINA19 0x19
+#define DINA21 0x21
+#define DINA29 0x29
+#define DINA31 0x31
+#define DINA39 0x39
+#define DINA41 0x41
+#define DINA49 0x49
+#define DINA51 0x51
+#define DINA59 0x59
+#define DINA61 0x61
+#define DINA69 0x69
+#define DINA71 0x71
+#define DINA79 0x79
+
+#define DINA25 0x25
+#define DINA2D 0x2d
+#define DINA35 0x35
+#define DINA3D 0x3d
+#define DINA4D 0x4d
+#define DINA55 0x55
+#define DINA5D 0x5D
+#define DINA6D 0x6d
+#define DINA75 0x75
+#define DINA7D 0x7d
+
+#define DINADC 0xdc
+#define DINAF2 0xf2
+#define DINAAB 0xab
+#define DINAAA 0xaa
+#define DINAF1 0xf1
+#define DINADF 0xdf
+#define DINADA 0xda
+#define DINAB1 0xb1
+#define DINAB9 0xb9
+#define DINAF3 0xf3
+#define DINA8B 0x8b
+#define DINAA3 0xa3
+#define DINA91 0x91
+#define DINAB6 0xb6
+#define DINAB4 0xb4
+
+
+#define DINC00 0x00
+#define DINC01 0x01
+#define DINC02 0x02
+#define DINC03 0x03
+#define DINC08 0x08
+#define DINC09 0x09
+#define DINC0A 0x0a
+#define DINC0B 0x0b
+#define DINC10 0x10
+#define DINC11 0x11
+#define DINC12 0x12
+#define DINC13 0x13
+#define DINC18 0x18
+#define DINC19 0x19
+#define DINC1A 0x1a
+#define DINC1B 0x1b
+
+#define DINC20 0x20
+#define DINC21 0x21
+#define DINC22 0x22
+#define DINC23 0x23
+#define DINC28 0x28
+#define DINC29 0x29
+#define DINC2A 0x2a
+#define DINC2B 0x2b
+#define DINC30 0x30
+#define DINC31 0x31
+#define DINC32 0x32
+#define DINC33 0x33
+#define DINC38 0x38
+#define DINC39 0x39
+#define DINC3A 0x3a
+#define DINC3B 0x3b
+
+#define DINC40 0x40
+#define DINC41 0x41
+#define DINC42 0x42
+#define DINC43 0x43
+#define DINC48 0x48
+#define DINC49 0x49
+#define DINC4A 0x4a
+#define DINC4B 0x4b
+#define DINC50 0x50
+#define DINC51 0x51
+#define DINC52 0x52
+#define DINC53 0x53
+#define DINC58 0x58
+#define DINC59 0x59
+#define DINC5A 0x5a
+#define DINC5B 0x5b
+
+#define DINC60 0x60
+#define DINC61 0x61
+#define DINC62 0x62
+#define DINC63 0x63
+#define DINC68 0x68
+#define DINC69 0x69
+#define DINC6A 0x6a
+#define DINC6B 0x6b
+#define DINC70 0x70
+#define DINC71 0x71
+#define DINC72 0x72
+#define DINC73 0x73
+#define DINC78 0x78
+#define DINC79 0x79
+#define DINC7A 0x7a
+#define DINC7B 0x7b
+#define DIND40 0x40
+#define DINA80 0x80
+#define DINA90 0x90
+#define DINAA0 0xa0
+#define DINAC9 0xc9
+#define DINACB 0xcb
+#define DINACD 0xcd
+#define DINACF 0xcf
+#define DINAC8 0xc8
+#define DINACA 0xca
+#define DINACC 0xcc
+#define DINACE 0xce
+#define DINAD8 0xd8
+#define DINADD 0xdd
+#define DINAF8 0xf0
+#define DINAFE 0xfe
+
+#define DINBF8 0xf8
+#define DINAC0 0xb0
+#define DINAC1 0xb1
+#define DINAC2 0xb4
+#define DINAC3 0xb5
+#define DINAC4 0xb8
+#define DINAC5 0xb9
+#define DINBC0 0xc0
+#define DINBC2 0xc2
+#define DINBC4 0xc4
+#define DINBC6 0xc6
+
+#endif
diff --git a/drivers/staging/iio/imu/mpu/dmpmap.h b/drivers/staging/iio/imu/mpu/dmpmap.h
new file mode 100644
index 0000000..28f59af
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu/dmpmap.h
@@ -0,0 +1,283 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    dmpmap.h
+ *      @brief   dmp map definition
+ *      @details This file is part of invensense mpu driver code
+ *
+ */
+#ifndef DMPMAP_H
+#define DMPMAP_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define DMP_PTAT    0
+#define DMP_XGYR    2
+#define DMP_YGYR    4
+#define DMP_ZGYR    6
+#define DMP_XACC    8
+#define DMP_YACC    10
+#define DMP_ZACC    12
+#define DMP_ADC1    14
+#define DMP_ADC2    16
+#define DMP_ADC3    18
+#define DMP_BIASUNC    20
+#define DMP_FIFORT    22
+#define DMP_INVGSFH    24
+#define DMP_INVGSFL    26
+#define DMP_1H    28
+#define DMP_1L    30
+#define DMP_BLPFSTCH    32
+#define DMP_BLPFSTCL    34
+#define DMP_BLPFSXH    36
+#define DMP_BLPFSXL    38
+#define DMP_BLPFSYH    40
+#define DMP_BLPFSYL    42
+#define DMP_BLPFSZH    44
+#define DMP_BLPFSZL    46
+#define DMP_BLPFMTC    48
+#define DMP_SMC    50
+#define DMP_BLPFMXH    52
+#define DMP_BLPFMXL    54
+#define DMP_BLPFMYH    56
+#define DMP_BLPFMYL    58
+#define DMP_BLPFMZH    60
+#define DMP_BLPFMZL    62
+#define DMP_BLPFC    64
+#define DMP_SMCTH    66
+#define DMP_0H2    68
+#define DMP_0L2    70
+#define DMP_BERR2H    72
+#define DMP_BERR2L    74
+#define DMP_BERR2NH    76
+#define DMP_SMCINC    78
+#define DMP_ANGVBXH    80
+#define DMP_ANGVBXL    82
+#define DMP_ANGVBYH    84
+#define DMP_ANGVBYL    86
+#define DMP_ANGVBZH    88
+#define DMP_ANGVBZL    90
+#define DMP_BERR1H    92
+#define DMP_BERR1L    94
+#define DMP_ATCH    96
+#define DMP_BIASUNCSF    98
+#define DMP_ACT2H    100
+#define DMP_ACT2L    102
+#define DMP_GSFH    104
+#define DMP_GSFL    106
+#define DMP_GH    108
+#define DMP_GL    110
+#define DMP_0_5H    112
+#define DMP_0_5L    114
+#define DMP_0_0H    116
+#define DMP_0_0L    118
+#define DMP_1_0H    120
+#define DMP_1_0L    122
+#define DMP_1_5H    124
+#define DMP_1_5L    126
+#define DMP_TMP1AH    128
+#define DMP_TMP1AL    130
+#define DMP_TMP2AH    132
+#define DMP_TMP2AL    134
+#define DMP_TMP3AH    136
+#define DMP_TMP3AL    138
+#define DMP_TMP4AH    140
+#define DMP_TMP4AL    142
+#define DMP_XACCW    144
+#define DMP_TMP5    146
+#define DMP_XACCB    148
+#define DMP_TMP8    150
+#define DMP_YACCB    152
+#define DMP_TMP9    154
+#define DMP_ZACCB    156
+#define DMP_TMP10    158
+#define DMP_DZH    160
+#define DMP_DZL    162
+#define DMP_XGCH    164
+#define DMP_XGCL    166
+#define DMP_YGCH    168
+#define DMP_YGCL    170
+#define DMP_ZGCH    172
+#define DMP_ZGCL    174
+#define DMP_YACCW    176
+#define DMP_TMP7    178
+#define DMP_AFB1H    180
+#define DMP_AFB1L    182
+#define DMP_AFB2H    184
+#define DMP_AFB2L    186
+#define DMP_MAGFBH    188
+#define DMP_MAGFBL    190
+#define DMP_QT1H    192
+#define DMP_QT1L    194
+#define DMP_QT2H    196
+#define DMP_QT2L    198
+#define DMP_QT3H    200
+#define DMP_QT3L    202
+#define DMP_QT4H    204
+#define DMP_QT4L    206
+#define DMP_CTRL1H    208
+#define DMP_CTRL1L    210
+#define DMP_CTRL2H    212
+#define DMP_CTRL2L    214
+#define DMP_CTRL3H    216
+#define DMP_CTRL3L    218
+#define DMP_CTRL4H    220
+#define DMP_CTRL4L    222
+#define DMP_CTRLS1    224
+#define DMP_CTRLSF1    226
+#define DMP_CTRLS2    228
+#define DMP_CTRLSF2    230
+#define DMP_CTRLS3    232
+#define DMP_CTRLSFNLL    234
+#define DMP_CTRLS4    236
+#define DMP_CTRLSFNL2    238
+#define DMP_CTRLSFNL    240
+#define DMP_TMP30    242
+#define DMP_CTRLSFJT    244
+#define DMP_TMP31    246
+#define DMP_TMP11    248
+#define DMP_CTRLSF2_2    250
+#define DMP_TMP12    252
+#define DMP_CTRLSF1_2    254
+#define DMP_PREVPTAT    256
+#define DMP_ACCZB    258
+#define DMP_ACCXB    264
+#define DMP_ACCYB    266
+#define DMP_1HB    272
+#define DMP_1LB    274
+#define DMP_0H    276
+#define DMP_0L    278
+#define DMP_ASR22H    280
+#define DMP_ASR22L    282
+#define DMP_ASR6H    284
+#define DMP_ASR6L    286
+#define DMP_TMP13    288
+#define DMP_TMP14    290
+#define DMP_FINTXH    292
+#define DMP_FINTXL    294
+#define DMP_FINTYH    296
+#define DMP_FINTYL    298
+#define DMP_FINTZH    300
+#define DMP_FINTZL    302
+#define DMP_TMP1BH    304
+#define DMP_TMP1BL    306
+#define DMP_TMP2BH    308
+#define DMP_TMP2BL    310
+#define DMP_TMP3BH    312
+#define DMP_TMP3BL    314
+#define DMP_TMP4BH    316
+#define DMP_TMP4BL    318
+#define DMP_STXG    320
+#define DMP_ZCTXG    322
+#define DMP_STYG    324
+#define DMP_ZCTYG    326
+#define DMP_STZG    328
+#define DMP_ZCTZG    330
+#define DMP_CTRLSFJT2    332
+#define DMP_CTRLSFJTCNT    334
+#define DMP_PVXG    336
+#define DMP_TMP15    338
+#define DMP_PVYG    340
+#define DMP_TMP16    342
+#define DMP_PVZG    344
+#define DMP_TMP17    346
+#define DMP_MNMFLAGH    352
+#define DMP_MNMFLAGL    354
+#define DMP_MNMTMH    356
+#define DMP_MNMTML    358
+#define DMP_MNMTMTHRH    360
+#define DMP_MNMTMTHRL    362
+#define DMP_MNMTHRH    364
+#define DMP_MNMTHRL    366
+#define DMP_ACCQD4H    368
+#define DMP_ACCQD4L    370
+#define DMP_ACCQD5H    372
+#define DMP_ACCQD5L    374
+#define DMP_ACCQD6H    376
+#define DMP_ACCQD6L    378
+#define DMP_ACCQD7H    380
+#define DMP_ACCQD7L    382
+#define DMP_ACCQD0H    384
+#define DMP_ACCQD0L    386
+#define DMP_ACCQD1H    388
+#define DMP_ACCQD1L    390
+#define DMP_ACCQD2H    392
+#define DMP_ACCQD2L    394
+#define DMP_ACCQD3H    396
+#define DMP_ACCQD3L    398
+#define DMP_XN2H    400
+#define DMP_XN2L    402
+#define DMP_XN1H    404
+#define DMP_XN1L    406
+#define DMP_YN2H    408
+#define DMP_YN2L    410
+#define DMP_YN1H    412
+#define DMP_YN1L    414
+#define DMP_YH    416
+#define DMP_YL    418
+#define DMP_B0H    420
+#define DMP_B0L    422
+#define DMP_A1H    424
+#define DMP_A1L    426
+#define DMP_A2H    428
+#define DMP_A2L    430
+#define DMP_SEM1    432
+#define DMP_FIFOCNT    434
+#define DMP_SH_TH_X    436
+#define DMP_PACKET    438
+#define DMP_SH_TH_Y    440
+#define DMP_FOOTER    442
+#define DMP_SH_TH_Z    444
+#define DMP_TEMP29    448
+#define DMP_TEMP30    450
+#define DMP_XACCB_PRE    452
+#define DMP_XACCB_PREL    454
+#define DMP_YACCB_PRE    456
+#define DMP_YACCB_PREL    458
+#define DMP_ZACCB_PRE    460
+#define DMP_ZACCB_PREL    462
+#define DMP_TMP22    464
+#define DMP_TAP_TIMER    466
+#define DMP_TAP_THX    468
+#define DMP_TAP_THY    472
+#define DMP_TAP_THZ    476
+#define DMP_TAPW_MIN    478
+#define DMP_TMP25    480
+#define DMP_TMP26    482
+#define DMP_TMP27    484
+#define DMP_TMP28    486
+#define DMP_ORIENT    488
+#define DMP_THRSH    490
+#define DMP_ENDIANH    492
+#define DMP_ENDIANL    494
+#define DMP_BLPFNMTCH    496
+#define DMP_BLPFNMTCL    498
+#define DMP_BLPFNMXH    500
+#define DMP_BLPFNMXL    502
+#define DMP_BLPFNMYH    504
+#define DMP_BLPFNMYL    506
+#define DMP_BLPFNMZH    508
+#define DMP_BLPFNMZL    510
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/drivers/staging/iio/imu/mpu/inv_mpu3050_iio.c b/drivers/staging/iio/imu/mpu/inv_mpu3050_iio.c
new file mode 100644
index 0000000..df12d78
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu/inv_mpu3050_iio.c
@@ -0,0 +1,295 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    inv_mpu3050_iio.c
+ *      @brief   A sysfs device driver for Invensense devices
+ *      @details This file is part of invensense mpu driver code
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_mpu_iio.h"
+#define MPU3050_NACK_MIN_TIME (2 * 1000)
+#define MPU3050_NACK_MAX_TIME (3 * 1000)
+
+#define MPU3050_ONE_MPU_TIME 20
+#define MPU3050_BOGUS_ADDR  0x7F
+int __attribute__((weak)) inv_register_mpu3050_slave(struct inv_mpu_iio_s *st)
+{
+	return 0;
+}
+
+int set_3050_bypass(struct inv_mpu_iio_s *st, bool enable)
+{
+	struct inv_reg_map_s *reg;
+	int result;
+	unsigned char b;
+
+	reg = &st->reg;
+	result = inv_i2c_read(st, reg->user_ctrl, 1, &b);
+	if (result)
+		return result;
+	if (((b & BIT_3050_AUX_IF_EN) == 0) && enable)
+		return 0;
+	if ((b & BIT_3050_AUX_IF_EN) && (enable == 0))
+		return 0;
+	b &= ~BIT_3050_AUX_IF_EN;
+	if (!enable) {
+		b |= BIT_3050_AUX_IF_EN;
+		result = inv_i2c_single_write(st, reg->user_ctrl, b);
+		return result;
+	} else {
+		/* Coming out of I2C is tricky due to several erratta.  Do not
+		* modify this algorithm
+		*/
+		/*
+		* 1) wait for the right time and send the command to change
+		* the aux i2c slave address to an invalid address that will
+		* get nack'ed
+		*
+		* 0x00 is broadcast.  0x7F is unlikely to be used by any aux.
+		*/
+		result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR,
+						MPU3050_BOGUS_ADDR);
+		if (result)
+			return result;
+		/*
+		* 2) wait enough time for a nack to occur, then go into
+		*    bypass mode:
+		*/
+		usleep_range(MPU3050_NACK_MIN_TIME, MPU3050_NACK_MAX_TIME);
+		result = inv_i2c_single_write(st, reg->user_ctrl, b);
+		if (result)
+			return result;
+		/*
+		* 3) wait for up to one MPU cycle then restore the slave
+		*    address
+		*/
+		msleep(MPU3050_ONE_MPU_TIME);
+
+		result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR,
+			st->plat_data.secondary_i2c_addr);
+		if (result)
+			return result;
+		result = inv_i2c_single_write(st, reg->user_ctrl, b);
+		if (result)
+			return result;
+		usleep_range(MPU3050_NACK_MIN_TIME, MPU3050_NACK_MAX_TIME);
+	}
+	return 0;
+}
+
+void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg)
+{
+	reg->fifo_en         = REG_3050_FIFO_EN;
+	reg->sample_rate_div = REG_3050_SAMPLE_RATE_DIV;
+	reg->lpf             = REG_3050_LPF;
+	reg->fifo_count_h    = REG_3050_FIFO_COUNT_H;
+	reg->fifo_r_w        = REG_3050_FIFO_R_W;
+	reg->user_ctrl       = REG_3050_USER_CTRL;
+	reg->pwr_mgmt_1      = REG_3050_PWR_MGMT_1;
+	reg->raw_gyro        = REG_3050_RAW_GYRO;
+	reg->raw_accl        = REG_3050_AUX_XOUT_H;
+	reg->temperature     = REG_3050_TEMPERATURE;
+	reg->int_enable      = REG_3050_INT_ENABLE;
+	reg->int_status      = REG_3050_INT_STATUS;
+}
+
+int inv_switch_3050_gyro_engine(struct inv_mpu_iio_s *st, bool en)
+{
+	struct inv_reg_map_s *reg;
+	unsigned char data, p;
+	int result;
+	reg = &st->reg;
+	if (en) {
+		data = INV_CLK_PLL;
+		p = (BITS_3050_POWER1 | data);
+		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+		if (result)
+			return result;
+		p = (BITS_3050_POWER2 | data);
+		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+		if (result)
+			return result;
+		p = data;
+		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+		msleep(SENSOR_UP_TIME);
+	} else {
+		p = BITS_3050_GYRO_STANDBY;
+		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+	}
+
+	return result;
+}
+
+int inv_switch_3050_accl_engine(struct inv_mpu_iio_s *st, bool en)
+{
+	int result;
+	if (NULL == st->mpu_slave)
+		return -EPERM;
+	if (en)
+		result = st->mpu_slave->resume(st);
+	else
+		result = st->mpu_slave->suspend(st);
+
+	return result;
+}
+
+/**
+ *  inv_init_config_mpu3050() - Initialize hardware, disable FIFO.
+ *  @st:	Device driver instance.
+ *  Initial configuration:
+ *  FSR: +/- 2000DPS
+ *  DLPF: 42Hz
+ *  FIFO rate: 50Hz
+ *  Clock source: Gyro PLL
+ */
+int inv_init_config_mpu3050(struct iio_dev *indio_dev)
+{
+	struct inv_reg_map_s *reg;
+	int result;
+	unsigned char data;
+	struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+
+	if (st->chip_config.is_asleep)
+		return -EPERM;
+	/*reading AUX VDDIO register */
+	result = inv_i2c_read(st, REG_3050_AUX_VDDIO, 1, &data);
+	if (result)
+		return result;
+	data &= ~BIT_3050_VDDIO;
+	if (st->plat_data.level_shifter)
+		data |= BIT_3050_VDDIO;
+	result = inv_i2c_single_write(st, REG_3050_AUX_VDDIO, data);
+	if (result)
+		return result;
+
+	reg = &st->reg;
+	result = set_inv_enable(indio_dev, false);
+	if (result)
+		return result;
+	/*2000dps full scale range*/
+	result = inv_i2c_single_write(st, reg->lpf,
+				(INV_FSR_2000DPS << GYRO_CONFIG_FSR_SHIFT)
+				| INV_FILTER_42HZ);
+	if (result)
+		return result;
+	st->chip_config.fsr = INV_FSR_2000DPS;
+	st->chip_config.lpf = INV_FILTER_42HZ;
+	result = inv_i2c_single_write(st, reg->sample_rate_div,
+					ONE_K_HZ/INIT_FIFO_RATE - 1);
+	if (result)
+		return result;
+	st->chip_config.fifo_rate = INIT_FIFO_RATE;
+	st->irq_dur_ns            = INIT_DUR_TIME;
+	st->chip_config.prog_start_addr = DMP_START_ADDR;
+	st->chip_config.gyro_enable = 1;
+	st->chip_config.gyro_fifo_enable = 1;
+	if ((SECONDARY_SLAVE_TYPE_ACCEL == st->plat_data.sec_slave_type) &&
+		st->mpu_slave) {
+		result = st->mpu_slave->setup(st);
+		if (result)
+			return result;
+		result = st->mpu_slave->set_fs(st, INV_FS_02G);
+		if (result)
+			return result;
+		result = st->mpu_slave->set_lpf(st, INIT_FIFO_RATE);
+		if (result)
+			return result;
+		st->chip_config.accl_enable = 1;
+		st->chip_config.accl_fifo_enable = 1;
+	}
+
+	return 0;
+}
+
+/**
+ *  set_power_mpu3050() - set power of mpu3050.
+ *  @st:	Device driver instance.
+ *  @power_on:  on/off
+ */
+int set_power_mpu3050(struct inv_mpu_iio_s *st, bool power_on)
+{
+	struct inv_reg_map_s *reg;
+	unsigned char data, p;
+	int result;
+	reg = &st->reg;
+	if (power_on) {
+		data = 0;
+	} else {
+		if (st->mpu_slave) {
+			result = st->mpu_slave->suspend(st);
+			if (result)
+				return result;
+		}
+		data = BIT_SLEEP;
+	}
+	if (st->chip_config.gyro_enable) {
+		p = (BITS_3050_POWER1 | INV_CLK_PLL);
+		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p);
+		if (result)
+			return result;
+
+		p = (BITS_3050_POWER2 | INV_CLK_PLL);
+		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p);
+		if (result)
+			return result;
+
+		p = INV_CLK_PLL;
+		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p);
+		if (result)
+			return result;
+
+		st->chip_config.clk_src = INV_CLK_PLL;
+	} else {
+		data |= (BITS_3050_GYRO_STANDBY | INV_CLK_INTERNAL);
+		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data);
+		if (result)
+			return result;
+		st->chip_config.clk_src = INV_CLK_INTERNAL;
+	}
+	if (power_on) {
+		msleep(POWER_UP_TIME);
+		if (st->mpu_slave) {
+			result = st->mpu_slave->resume(st);
+			if (result)
+				return result;
+		}
+	}
+	st->chip_config.is_asleep = !power_on;
+
+	return 0;
+}
+/**
+ *  @}
+ */
+
diff --git a/drivers/staging/iio/imu/mpu/inv_mpu_core.c b/drivers/staging/iio/imu/mpu/inv_mpu_core.c
new file mode 100644
index 0000000..e1e3138
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu/inv_mpu_core.c
@@ -0,0 +1,2038 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    inv_mpu_core.c
+ *      @brief   A sysfs device driver for Invensense devices
+ *      @details This driver currently works for the MPU3050/MPU6050/MPU9150
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include "inv_mpu_iio.h"
+#include "../../sysfs.h"
+
+s64 get_time_ns(void)
+{
+	struct timespec ts;
+	ktime_get_ts(&ts);
+	return timespec_to_ns(&ts);
+}
+
+static const short AKM8975_ST_Lower[3] = {-100, -100, -1000};
+static const short AKM8975_ST_Upper[3] = {100, 100, -300};
+
+static const short AKM8972_ST_Lower[3] = {-50, -50, -500};
+static const short AKM8972_ST_Upper[3] = {50, 50, -100};
+
+static const short AKM8963_ST_Lower[3] = {-200, -200, -3200};
+static const short AKM8963_ST_Upper[3] = {200, 200, -800};
+
+static const struct inv_hw_s hw_info[INV_NUM_PARTS] = {
+	{119, "ITG3500"},
+	{ 63, "MPU3050"},
+	{117, "MPU6050"},
+	{118, "MPU9150"},
+	{119, "MPU6500"},
+};
+
+static void inv_setup_reg(struct inv_reg_map_s *reg)
+{
+	reg->sample_rate_div	= REG_SAMPLE_RATE_DIV;
+	reg->lpf		= REG_CONFIG;
+	reg->bank_sel		= REG_BANK_SEL;
+	reg->user_ctrl		= REG_USER_CTRL;
+	reg->fifo_en		= REG_FIFO_EN;
+	reg->gyro_config	= REG_GYRO_CONFIG;
+	reg->accl_config	= REG_ACCEL_CONFIG;
+	reg->fifo_count_h	= REG_FIFO_COUNT_H;
+	reg->fifo_r_w		= REG_FIFO_R_W;
+	reg->raw_gyro		= REG_RAW_GYRO;
+	reg->raw_accl		= REG_RAW_ACCEL;
+	reg->temperature	= REG_TEMPERATURE;
+	reg->int_enable		= REG_INT_ENABLE;
+	reg->int_status		= REG_INT_STATUS;
+	reg->pwr_mgmt_1		= REG_PWR_MGMT_1;
+	reg->pwr_mgmt_2		= REG_PWR_MGMT_2;
+	reg->mem_start_addr	= REG_MEM_START_ADDR;
+	reg->mem_r_w		= REG_MEM_RW;
+	reg->prgm_strt_addrh	= REG_PRGM_STRT_ADDRH;
+};
+
+/**
+ *  inv_i2c_read() - Read one or more bytes from the device registers.
+ *  @st:	Device driver instance.
+ *  @reg:	First device register to be read from.
+ *  @length:	Number of bytes to read.
+ *  @data:	Data read from device.
+ *  NOTE:This is not re-implementation of i2c_smbus_read because i2c
+ *       address could be specified in this case. We could have two different
+ *       i2c address due to secondary i2c interface.
+ */
+int inv_i2c_read_base(struct inv_mpu_iio_s *st, unsigned short i2c_addr,
+	u8 reg, unsigned short length, u8 *data)
+{
+	struct i2c_msg msgs[2];
+	int res;
+
+	if (!data)
+		return -EINVAL;
+
+	msgs[0].addr = i2c_addr;
+	msgs[0].flags = 0;	/* write */
+	msgs[0].buf = &reg;
+	msgs[0].len = 1;
+
+	msgs[1].addr = i2c_addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].buf = data;
+	msgs[1].len = length;
+
+	pr_debug("%s RD%02X%02X%02X\n", st->hw->name, i2c_addr, reg, length);
+	res = i2c_transfer(st->sl_handle, msgs, 2);
+	if (res < 2) {
+		if (res >= 0)
+			res = -EIO;
+		return res;
+	} else {
+#ifdef CONFIG_INV_TESTING
+		st->i2c_writecount += 3; /* addr + reg + addr */
+		st->i2c_readcount += length; /* addr + data */
+#endif
+		return 0;
+	}
+}
+
+/**
+ *  inv_i2c_single_write() - Write a byte to a device register.
+ *  @st:	Device driver instance.
+ *  @reg:	Device register to be written to.
+ *  @data:	Byte to write to device.
+ *  NOTE:This is not re-implementation of i2c_smbus_write because i2c
+ *       address could be specified in this case. We could have two different
+ *       i2c address due to secondary i2c interface.
+ */
+int inv_i2c_single_write_base(struct inv_mpu_iio_s *st,
+	unsigned short i2c_addr, u8 reg, u8 data)
+{
+	u8 tmp[2];
+	struct i2c_msg msg;
+	int res;
+
+	tmp[0] = reg;
+	tmp[1] = data;
+
+	msg.addr = i2c_addr;
+	msg.flags = 0;	/* write */
+	msg.buf = tmp;
+	msg.len = 2;
+
+	pr_debug("%s WS%02X%02X%02X\n", st->hw->name, i2c_addr, reg, data);
+	res = i2c_transfer(st->sl_handle, &msg, 1);
+	if (res < 1) {
+		if (res == 0)
+			res = -EIO;
+		return res;
+	} else {
+#ifdef CONFIG_INV_TESTING
+		st->i2c_writecount += 3; /* addr + reg + data */
+#endif
+		return 0;
+	}
+}
+
+static int set_power_itg(struct inv_mpu_iio_s *st, bool power_on)
+{
+	struct inv_reg_map_s *reg;
+	u8 data;
+	int result;
+
+	reg = &st->reg;
+	if (power_on)
+		data = 0;
+	else
+		data = BIT_SLEEP;
+	if (st->chip_config.lpa_mode)
+		data |= BIT_CYCLE;
+	if (st->chip_config.gyro_enable) {
+		result = inv_i2c_single_write(st,
+			reg->pwr_mgmt_1, data | INV_CLK_PLL);
+		if (result)
+			return result;
+		st->chip_config.clk_src = INV_CLK_PLL;
+	} else {
+		result = inv_i2c_single_write(st,
+			reg->pwr_mgmt_1, data | INV_CLK_INTERNAL);
+		if (result)
+			return result;
+		st->chip_config.clk_src = INV_CLK_INTERNAL;
+	}
+
+	if (power_on) {
+		msleep(POWER_UP_TIME);
+		data = 0;
+		if (!st->chip_config.accl_enable)
+			data |= BIT_PWR_ACCL_STBY;
+		if (!st->chip_config.gyro_enable)
+			data |= BIT_PWR_GYRO_STBY;
+		if (INV_MPU6500 != st->chip_type)
+			data |= (st->chip_config.lpa_freq << LPA_FREQ_SHIFT);
+
+		result = inv_i2c_single_write(st, reg->pwr_mgmt_2, data);
+		if (result) {
+			inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_SLEEP);
+			return result;
+		}
+		msleep(SENSOR_UP_TIME);
+	}
+	st->chip_config.is_asleep = !power_on;
+
+	return 0;
+}
+
+/**
+ *  inv_init_config() - Initialize hardware, disable FIFO.
+ *  @indio_dev:	Device driver instance.
+ *  Initial configuration:
+ *  FSR: +/- 2000DPS
+ *  DLPF: 42Hz
+ *  FIFO rate: 50Hz
+ *  Clock source: Gyro PLL
+ */
+static int inv_init_config(struct iio_dev *indio_dev)
+{
+	struct inv_reg_map_s *reg;
+	int result;
+	struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+
+	if (st->chip_config.is_asleep)
+		return -EPERM;
+	reg = &st->reg;
+	result = set_inv_enable(indio_dev, false);
+	if (result)
+		return result;
+
+	result = inv_i2c_single_write(st, reg->gyro_config,
+		INV_FSR_2000DPS << GYRO_CONFIG_FSR_SHIFT);
+	if (result)
+		return result;
+	st->chip_config.fsr = INV_FSR_2000DPS;
+
+	result = inv_i2c_single_write(st, reg->lpf, INV_FILTER_42HZ);
+	if (result)
+		return result;
+	st->chip_config.lpf = INV_FILTER_42HZ;
+
+	result = inv_i2c_single_write(st, reg->sample_rate_div,
+					ONE_K_HZ / INIT_FIFO_RATE - 1);
+	if (result)
+		return result;
+	st->chip_config.fifo_rate = INIT_FIFO_RATE;
+	st->irq_dur_ns            = INIT_DUR_TIME;
+	st->chip_config.prog_start_addr = DMP_START_ADDR;
+	st->chip_config.gyro_enable = 1;
+	st->chip_config.gyro_fifo_enable = 1;
+	st->chip_config.dmp_output_rate = INIT_DMP_OUTPUT_RATE;
+	if (INV_ITG3500 != st->chip_type) {
+		st->chip_config.accl_enable = 1;
+		st->chip_config.accl_fifo_enable = 1;
+		st->chip_config.accl_fs = INV_FS_02G;
+		result = inv_i2c_single_write(st, reg->accl_config,
+			(INV_FS_02G << ACCL_CONFIG_FSR_SHIFT));
+		if (result)
+			return result;
+		st->tap.time = INIT_TAP_TIME;
+		st->tap.thresh = INIT_TAP_THRESHOLD;
+		st->tap.min_count = INIT_TAP_MIN_COUNT;
+	}
+
+	return 0;
+}
+
+/**
+ *  inv_compass_scale_show() - show compass scale.
+ */
+static int inv_compass_scale_show(struct inv_mpu_iio_s *st, int *scale)
+{
+	if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id)
+		*scale = DATA_AKM8975_SCALE;
+	else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id)
+		*scale = DATA_AKM8972_SCALE;
+	else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id)
+		if (st->compass_scale)
+			*scale = DATA_AKM8963_SCALE1;
+		else
+			*scale = DATA_AKM8963_SCALE0;
+	else
+		return -EINVAL;
+
+	return IIO_VAL_INT;
+}
+
+/**
+ *  mpu_read_raw() - read raw method.
+ */
+static int mpu_read_raw(struct iio_dev *indio_dev,
+			struct iio_chan_spec const *chan,
+			int *val, int *val2, long mask)
+{
+	struct inv_mpu_iio_s  *st = iio_priv(indio_dev);
+	int result;
+	if (st->chip_config.is_asleep)
+		return -EINVAL;
+	switch (mask) {
+	case 0:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			*val = st->raw_gyro[chan->channel2 - IIO_MOD_X];
+			return IIO_VAL_INT;
+		case IIO_ACCEL:
+			*val = st->raw_accel[chan->channel2 - IIO_MOD_X];
+			return IIO_VAL_INT;
+		case IIO_MAGN:
+			*val = st->raw_compass[chan->channel2 - IIO_MOD_X];
+			return IIO_VAL_INT;
+		case IIO_QUATERNION:
+			if (IIO_MOD_R == chan->channel2)
+				*val = st->raw_quaternion[0];
+			else
+				*val = st->raw_quaternion[chan->channel2 -
+							  IIO_MOD_X + 1];
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+		return -EINVAL;
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+		{
+			const s16 gyro_scale_6050[] = {250, 500, 1000, 2000};
+			const s16 gyro_scale_6500[] = {250, 1000, 2000, 4000};
+			if (INV_MPU6500 == st->chip_type)
+				*val = gyro_scale_6500[st->chip_config.fsr];
+			else
+				*val = gyro_scale_6050[st->chip_config.fsr];
+			return IIO_VAL_INT;
+		}
+		case IIO_ACCEL:
+		{
+			const s16 accel_scale[] = {2, 4, 8, 16};
+			*val = accel_scale[st->chip_config.accl_fs];
+			return IIO_VAL_INT;
+		}
+		case IIO_MAGN:
+			return inv_compass_scale_show(st, val);
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_CALIBBIAS:
+		/* return bias=0 for both accel and gyro for MPU6500;
+		   self test not supported yet */
+		if (INV_MPU6500 == st->chip_type) {
+			*val = 0;
+			return IIO_VAL_INT;
+		}
+		if (st->chip_config.self_test_run_once == 0) {
+			result = inv_do_test(st, 0,  st->gyro_bias,
+				st->accel_bias);
+			/* Reset Accel and Gyro full scale range
+			   back to default value */
+			inv_recover_setting(st);
+			if (result)
+				return result;
+			st->chip_config.self_test_run_once = 1;
+		}
+
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			*val = st->gyro_bias[chan->channel2 - IIO_MOD_X];
+			return IIO_VAL_INT;
+		case IIO_ACCEL:
+			*val = st->accel_bias[chan->channel2 - IIO_MOD_X] *
+					st->chip_info.multi;
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+/**
+ *  inv_write_fsr() - Configure the gyro's scale range.
+ */
+static int inv_write_fsr(struct inv_mpu_iio_s *st, int fsr)
+{
+	struct inv_reg_map_s *reg;
+	int result;
+	reg = &st->reg;
+	if ((fsr < 0) || (fsr > MAX_GYRO_FS_PARAM))
+		return -EINVAL;
+	if (fsr == st->chip_config.fsr)
+		return 0;
+
+	if (INV_MPU3050 == st->chip_type)
+		result = inv_i2c_single_write(st, reg->lpf,
+			(fsr << GYRO_CONFIG_FSR_SHIFT) | st->chip_config.lpf);
+	else
+		result = inv_i2c_single_write(st, reg->gyro_config,
+			fsr << GYRO_CONFIG_FSR_SHIFT);
+
+	if (result)
+		return result;
+	st->chip_config.fsr = fsr;
+
+	return 0;
+}
+
+/**
+ *  inv_write_accel_fs() - Configure the accelerometer's scale range.
+ */
+static int inv_write_accel_fs(struct inv_mpu_iio_s *st, int fs)
+{
+	int result;
+	struct inv_reg_map_s *reg;
+	reg = &st->reg;
+
+	if (fs < 0 || fs > MAX_ACCL_FS_PARAM)
+		return -EINVAL;
+	if (fs == st->chip_config.accl_fs)
+		return 0;
+	if (INV_MPU3050 == st->chip_type)
+		result = st->mpu_slave->set_fs(st, fs);
+	else
+		result = inv_i2c_single_write(st, reg->accl_config,
+				(fs << ACCL_CONFIG_FSR_SHIFT));
+	if (result)
+		return result;
+
+	st->chip_config.accl_fs = fs;
+
+	return 0;
+}
+
+/**
+ *  inv_write_compass_scale() - Configure the compass's scale range.
+ */
+static int inv_write_compass_scale(struct inv_mpu_iio_s  *st, int data)
+{
+	char d, en;
+	int result;
+	if (COMPASS_ID_AK8963 != st->plat_data.sec_slave_id)
+		return 0;
+	en = !!data;
+	if (st->compass_scale == en)
+		return 0;
+	d = (DATA_AKM_MODE_SM | (st->compass_scale << AKM8963_SCALE_SHIFT));
+	result = inv_i2c_single_write(st, REG_I2C_SLV1_DO, d);
+	if (result)
+		return result;
+	st->compass_scale = en;
+
+	return 0;
+}
+
+static inline int check_enable(struct inv_mpu_iio_s  *st)
+{
+	return st->chip_config.is_asleep | st->chip_config.enable;
+}
+
+/**
+ *  mpu_write_raw() - write raw method.
+ */
+static int mpu_write_raw(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int val,
+			       int val2,
+			       long mask) {
+	struct inv_mpu_iio_s  *st = iio_priv(indio_dev);
+	if (check_enable(st))
+		return -EPERM;
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			return inv_write_fsr(st, val);
+		case IIO_ACCEL:
+			return inv_write_accel_fs(st, val);
+		case IIO_MAGN:
+			return inv_write_compass_scale(st, val);
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ *  inv_set_lpf() - set low pass filer based on fifo rate.
+ */
+static int inv_set_lpf(struct inv_mpu_iio_s *st, int rate)
+{
+	const short hz[] = {188, 98, 42, 20, 10, 5};
+	const int   d[] = {INV_FILTER_188HZ, INV_FILTER_98HZ,
+			INV_FILTER_42HZ, INV_FILTER_20HZ,
+			INV_FILTER_10HZ, INV_FILTER_5HZ};
+	int i, h, data, result;
+	struct inv_reg_map_s *reg;
+	reg = &st->reg;
+	h = (rate >> 1);
+	i = 0;
+	while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1))
+		i++;
+	data = d[i];
+	if (INV_MPU3050 == st->chip_type) {
+		if (st->mpu_slave != NULL) {
+			result = st->mpu_slave->set_lpf(st, rate);
+			if (result)
+				return result;
+		}
+		result = inv_i2c_single_write(st, reg->lpf, data |
+			(st->chip_config.fsr << GYRO_CONFIG_FSR_SHIFT));
+	} else {
+		result = inv_i2c_single_write(st, reg->lpf, data);
+	}
+	if (result)
+		return result;
+	st->chip_config.lpf = data;
+
+	return 0;
+}
+
+/**
+ *  inv_fifo_rate_store() - Set fifo rate.
+ */
+static ssize_t inv_fifo_rate_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	unsigned long fifo_rate;
+	u8 data;
+	int result;
+	struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+	struct inv_reg_map_s *reg;
+	reg = &st->reg;
+
+	if (check_enable(st))
+		return -EPERM;
+	if (kstrtoul(buf, 10, &fifo_rate))
+		return -EINVAL;
+	if ((fifo_rate < MIN_FIFO_RATE) || (fifo_rate > MAX_FIFO_RATE))
+		return -EINVAL;
+	if (fifo_rate == st->chip_config.fifo_rate)
+		return count;
+	if (st->chip_config.has_compass) {
+		st->compass_divider = COMPASS_RATE_SCALE * fifo_rate /
+					ONE_K_HZ;
+		if (st->compass_divider > 0)
+			st->compass_divider -= 1;
+		st->compass_counter = 0;
+	}
+	data = ONE_K_HZ / fifo_rate - 1;
+	result = inv_i2c_single_write(st, reg->sample_rate_div, data);
+	if (result)
+		return result;
+	st->chip_config.fifo_rate = fifo_rate;
+	result = inv_set_lpf(st, fifo_rate);
+	if (result)
+		return result;
+	st->irq_dur_ns = (data + 1) * NSEC_PER_MSEC;
+
+	return count;
+}
+
+/**
+ *  inv_fifo_rate_show() - Get the current sampling rate.
+ */
+static ssize_t inv_fifo_rate_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+
+	return sprintf(buf, "%d\n", st->chip_config.fifo_rate);
+}
+
+/**
+ *  inv_power_state_store() - Turn device on/off.
+ */
+static ssize_t inv_power_state_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	int result;
+	unsigned long power_state;
+	struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+	if (kstrtoul(buf, 10, &power_state))
+		return -EINVAL;
+	if ((!power_state) == st->chip_config.is_asleep)
+		return count;
+	result = st->set_power_state(st, power_state);
+
+	return count;
+}
+
+/**
+ *  inv_reg_dump_show() - Register dump for testing.
+ */
+static ssize_t inv_reg_dump_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int ii;
+	char data;
+	ssize_t bytes_printed = 0;
+	struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+
+	for (ii = 0; ii < st->hw->num_reg; ii++) {
+		/* don't read fifo r/w register */
+		if (ii == st->reg.fifo_r_w)
+			data = 0;
+		else
+			inv_i2c_read(st, ii, 1, &data);
+		bytes_printed += sprintf(buf + bytes_printed, "%#2x: %#2x\n",
+					 ii, data);
+	}
+
+	return bytes_printed;
+}
+
+static inline int write_be32_key_to_mem(struct inv_mpu_iio_s *st,
+					u32 data, int key)
+{
+	cpu_to_be32s(&data);
+	return mem_w_key(key, 4, (u8 *)&data);
+}
+
+/**
+ * inv_dmp_attr_store() -  calling this function will store current
+ *                        dmp parameter settings
+ */
+static ssize_t inv_dmp_attr_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int result, data;
+	char d[4];
+	if (st->chip_config.is_asleep | (!st->chip_config.firmware_loaded))
+			return -EPERM;
+	result = kstrtoint(buf, 10, &data);
+	if (result)
+		return result;
+	switch (this_attr->address) {
+	case ATTR_DMP_FLICK_LOWER:
+		result = write_be32_key_to_mem(st, data, KEY_FLICK_LOWER);
+		if (result)
+			return result;
+		st->flick.lower = data;
+		break;
+	case ATTR_DMP_FLICK_UPPER:
+		result = write_be32_key_to_mem(st, data, KEY_FLICK_UPPER);
+		if (result)
+			return result;
+		st->flick.upper = data;
+		break;
+	case ATTR_DMP_FLICK_COUNTER:
+		result = write_be32_key_to_mem(st, data, KEY_FLICK_COUNTER);
+		if (result)
+			return result;
+		st->flick.counter = data;
+		break;
+	case ATTR_DMP_FLICK_INT_ON:
+		if (data)
+			/* Use interrupt*/
+			d[0] = DIND40+4;
+		else
+			d[0] = DINAA0+8;
+		result = mem_w_key(KEY_CGNOTICE_INTR, 1, d);
+		if (result)
+			return result;
+		st->chip_config.flick_int_on = !!data;
+		break;
+	case ATTR_DMP_FLICK_AXIS:
+		if (data == 0)
+			d[0] = DINBC2;
+		else if (data == 2)
+			d[2] = DINBC6;
+		else
+			d[0] = DINBC4;
+		result = mem_w_key(KEY_CFG_FLICK_IN, 1, d);
+		if (result)
+			return result;
+		st->flick.axis = data;
+		break;
+	case ATTR_DMP_FLICK_MSG_ON:
+		if (data)
+			data = DATA_MSG_ON;
+		result = write_be32_key_to_mem(st, data, KEY_FLICK_MSG);
+		if (result)
+			return result;
+		st->flick.msg_on = data;
+		break;
+	case ATTR_DMP_PEDOMETER_STEPS:
+		result = write_be32_key_to_mem(st, data, KEY_D_PEDSTD_STEPCTR);
+		if (result)
+			return result;
+		break;
+	case ATTR_DMP_PEDOMETER_TIME:
+		result = write_be32_key_to_mem(st, data, KEY_D_PEDSTD_TIMECTR);
+		if (result)
+			return result;
+		break;
+	case ATTR_DMP_TAP_THRESHOLD: {
+		const char ax[] = {INV_TAP_AXIS_X, INV_TAP_AXIS_Y,
+							INV_TAP_AXIS_Z};
+		int i;
+		if (data < 0 || data > USHRT_MAX)
+			return -EINVAL;
+		for (i = 0; i < ARRAY_SIZE(ax); i++) {
+			result = inv_set_tap_threshold_dmp(st, ax[i], data);
+			if (result)
+				return result;
+		}
+		st->tap.thresh = data;
+		break;
+	}
+	case ATTR_DMP_TAP_MIN_COUNT:
+		if (data < 0 || data > USHRT_MAX)
+			return -EINVAL;
+		result = inv_set_min_taps_dmp(st, data);
+		if (result)
+			return result;
+		st->tap.min_count = data;
+		break;
+	case ATTR_DMP_TAP_ON:
+		result = inv_enable_tap_dmp(st, !!data);
+		if (result)
+			return result;
+		st->chip_config.tap_on = !!data;
+		break;
+	case ATTR_DMP_TAP_TIME:
+		if (data < 0 || data > USHRT_MAX)
+			return -EINVAL;
+		result = inv_set_tap_time_dmp(st, data);
+		if (result)
+			return result;
+		st->tap.time = data;
+		break;
+	case ATTR_DMP_ON:
+		st->chip_config.dmp_on = !!data;
+		break;
+	case ATTR_DMP_INT_ON:
+		st->chip_config.dmp_int_on = !!data;
+		break;
+	case ATTR_DMP_EVENT_INT_ON:
+		result = inv_set_interrupt_on_gesture_event(st, !!data);
+		if (result)
+			return result;
+		st->chip_config.dmp_event_int_on = !!data;
+		break;
+	case ATTR_DMP_OUTPUT_RATE:
+		if (data <= 0 || data > USHRT_MAX)
+			return -EINVAL;
+		result = inv_set_fifo_rate(st, data);
+		if (result)
+			return result;
+		if (st->chip_config.has_compass) {
+			st->compass_dmp_divider = COMPASS_RATE_SCALE * data /
+							ONE_K_HZ;
+			if (st->compass_dmp_divider > 0)
+				st->compass_dmp_divider -= 1;
+		}
+		st->chip_config.dmp_output_rate = data;
+		break;
+	case ATTR_DMP_ORIENTATION_ON:
+		result = inv_enable_orientation_dmp(st, !!data);
+		if (result)
+			return result;
+		st->chip_config.orientation_on = !!data;
+		break;
+	case ATTR_DMP_DISPLAY_ORIENTATION_ON:
+		result = inv_set_display_orient_interrupt_dmp(st, !!data);
+		if (result)
+			return result;
+		st->chip_config.display_orient_on = !!data;
+		break;
+	default:
+		return -EPERM;
+	}
+
+	return count;
+}
+
+/**
+ * inv_attr_show() -  calling this function will show current
+ *                        dmp parameters.
+ */
+static ssize_t inv_attr_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	char d[4];
+	int result, data;
+	signed char *m;
+	u8 *key;
+	int i;
+
+	switch (this_attr->address) {
+	case ATTR_DMP_FLICK_LOWER:
+		return sprintf(buf, "%d\n", st->flick.lower);
+	case ATTR_DMP_FLICK_UPPER:
+		return sprintf(buf, "%d\n", st->flick.upper);
+	case ATTR_DMP_FLICK_COUNTER:
+		return sprintf(buf, "%d\n", st->flick.counter);
+	case ATTR_DMP_FLICK_INT_ON:
+		return sprintf(buf, "%d\n", st->chip_config.flick_int_on);
+	case ATTR_DMP_FLICK_AXIS:
+		return sprintf(buf, "%d\n", st->flick.axis);
+	case ATTR_DMP_FLICK_MSG_ON:
+		return sprintf(buf, "%d\n", st->flick.msg_on);
+	case ATTR_DMP_PEDOMETER_STEPS:
+		result = mpu_memory_read(st->sl_handle, st->i2c_addr,
+			inv_dmp_get_address(KEY_D_PEDSTD_STEPCTR), 4, d);
+		if (result)
+			return result;
+		data = be32_to_cpup((int *)d);
+		return sprintf(buf, "%d\n", data);
+	case ATTR_DMP_PEDOMETER_TIME:
+		result = mpu_memory_read(st->sl_handle, st->i2c_addr,
+			inv_dmp_get_address(KEY_D_PEDSTD_TIMECTR), 4, d);
+		if (result)
+			return result;
+		data = be32_to_cpup((int *)d);
+		return sprintf(buf, "%d\n", data * MS_PER_DMP_TICK);
+	case ATTR_DMP_TAP_THRESHOLD:
+		return sprintf(buf, "%d\n", st->tap.thresh);
+	case ATTR_DMP_TAP_MIN_COUNT:
+		return sprintf(buf, "%d\n", st->tap.min_count);
+	case ATTR_DMP_TAP_ON:
+		return sprintf(buf, "%d\n", st->chip_config.tap_on);
+	case ATTR_DMP_TAP_TIME:
+		return sprintf(buf, "%d\n", st->tap.time);
+	case ATTR_DMP_ON:
+		return sprintf(buf, "%d\n", st->chip_config.dmp_on);
+	case ATTR_DMP_INT_ON:
+		return sprintf(buf, "%d\n", st->chip_config.dmp_int_on);
+	case ATTR_DMP_EVENT_INT_ON:
+		return sprintf(buf, "%d\n", st->chip_config.dmp_event_int_on);
+	case ATTR_DMP_OUTPUT_RATE:
+		return sprintf(buf, "%d\n", st->chip_config.dmp_output_rate);
+	case ATTR_DMP_ORIENTATION_ON:
+		return sprintf(buf, "%d\n", st->chip_config.orientation_on);
+	case ATTR_DMP_QUATERNION_ON:
+		return sprintf(buf, "%d\n", st->chip_config.quaternion_on);
+	case ATTR_DMP_DISPLAY_ORIENTATION_ON:
+		return sprintf(buf, "%d\n",
+			st->chip_config.display_orient_on);
+	case ATTR_LPA_MODE:
+		return sprintf(buf, "%d\n", st->chip_config.lpa_mode);
+	case ATTR_LPA_FREQ:{
+		const char *f[] = {"1.25", "5", "20", "40"};
+		const char *f_6500[] = {"0.3125", "0.625", "1.25",
+					       "2.5", "5", "10", "20", "40",
+					       "80", "160", "320", "640"};
+		if (INV_MPU6500 == st->chip_type)
+			return sprintf(buf, "%s\n",
+				       f_6500[st->chip_config.lpa_freq]);
+		else
+			return sprintf(buf, "%s\n",
+				       f[st->chip_config.lpa_freq]);
+	}
+	case ATTR_CLK_SRC:
+		if (INV_CLK_INTERNAL == st->chip_config.clk_src)
+			return sprintf(buf, "INTERNAL\n");
+		else if (INV_CLK_PLL == st->chip_config.clk_src)
+			return sprintf(buf, "Gyro PLL\n");
+		else
+			return -EPERM;
+	case ATTR_SELF_TEST:
+		if (INV_MPU3050 == st->chip_type)
+			result = 1;
+		else if (INV_MPU6500 == st->chip_type)
+			result = inv_hw_self_test_6500(st);
+		else
+			result = inv_hw_self_test(st);
+		return sprintf(buf, "%d\n", result);
+	case ATTR_KEY:
+		key = st->plat_data.key;
+		result = 0;
+		for (i = 0; i < 16; i++)
+			result += sprintf(buf + result, "%02x", key[i]);
+		result += sprintf(buf + result, "\n");
+		return result;
+
+	case ATTR_GYRO_MATRIX:
+		m = st->plat_data.orientation;
+		return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+			m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+	case ATTR_ACCL_MATRIX:
+		if (st->plat_data.sec_slave_type == SECONDARY_SLAVE_TYPE_ACCEL)
+			m = st->plat_data.secondary_orientation;
+		else
+			m = st->plat_data.orientation;
+		return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+			m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+	case ATTR_COMPASS_MATRIX:
+		if (st->plat_data.sec_slave_type ==
+				SECONDARY_SLAVE_TYPE_COMPASS)
+			m = st->plat_data.secondary_orientation;
+		else
+			return -ENODEV;
+		return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+			m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+	case ATTR_GYRO_ENABLE:
+		return sprintf(buf, "%d\n", st->chip_config.gyro_enable);
+	case ATTR_ACCL_ENABLE:
+		return sprintf(buf, "%d\n", st->chip_config.accl_enable);
+	case ATTR_COMPASS_ENABLE:
+		return sprintf(buf, "%d\n", st->chip_config.compass_enable);
+	case ATTR_POWER_STATE:
+		return sprintf(buf, "%d\n", !st->chip_config.is_asleep);
+	case ATTR_FIRMWARE_LOADED:
+		return sprintf(buf, "%d\n", st->chip_config.firmware_loaded);
+#ifdef CONFIG_INV_TESTING
+	case ATTR_I2C_COUNTERS:
+		return scnprintf(buf, PAGE_SIZE, "%ld.%ld %ld %ld\n",
+			jiffies / HZ, jiffies % HZ, st->i2c_readcount,
+			st->i2c_writecount);
+	case ATTR_REG_WRITE:
+		return sprintf(buf, "1\n");
+#endif
+	default:
+		return -EPERM;
+	}
+}
+
+/**
+ * inv_dmp_flick_show() -  calling this function will show flick event.
+ *                         This event must use poll.
+ */
+static ssize_t inv_dmp_flick_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "1\n");
+}
+
+/**
+ * inv_dmp_orient_show() -  calling this function will show orientation
+ *                         This event must use poll.
+ */
+static ssize_t inv_dmp_orient_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+	return sprintf(buf, "%d\n", st->orient_data);
+}
+
+/**
+ * inv_dmp_display_orient_show() -  calling this function will
+ *			show orientation This event must use poll.
+ */
+static ssize_t inv_dmp_display_orient_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+	return sprintf(buf, "%d\n", st->display_orient_data);
+}
+
+/**
+ * inv_dmp_tap_show() -  calling this function will show tap
+ *                         This event must use poll.
+ */
+static ssize_t inv_dmp_tap_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+	return sprintf(buf, "%d\n", st->tap_data);
+}
+
+/**
+ *  inv_temperature_show() - Read temperature data directly from registers.
+ */
+static ssize_t inv_temperature_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+	struct inv_reg_map_s *reg;
+	int result;
+	short temp;
+	long scale_t;
+	u8 data[2];
+	reg = &st->reg;
+
+	if (st->chip_config.is_asleep)
+		return -EPERM;
+	result = inv_i2c_read(st, reg->temperature, 2, data);
+	if (result) {
+		pr_err("Could not read temperature register.\n");
+		return result;
+	}
+	temp = (signed short)(be16_to_cpup((short *)&data[0]));
+
+	if (INV_MPU3050 == st->chip_type)
+		scale_t = MPU3050_TEMP_OFFSET +
+			inv_q30_mult((int)temp << MPU_TEMP_SHIFT,
+				     MPU3050_TEMP_SCALE);
+	else
+		scale_t = MPU6050_TEMP_OFFSET +
+			inv_q30_mult((int)temp << MPU_TEMP_SHIFT,
+				     MPU6050_TEMP_SCALE);
+#ifdef CONFIG_INV_TESTING
+	 st->i2c_tempreads++;
+#endif
+	return sprintf(buf, "%ld %lld\n", scale_t, get_time_ns());
+}
+
+/**
+ * inv_firmware_loaded() -  calling this function will change
+ *                        firmware load
+ */
+static int inv_firmware_loaded(struct inv_mpu_iio_s *st, int data)
+{
+	if (data)
+		return -EINVAL;
+	st->chip_config.firmware_loaded = 0;
+	st->chip_config.dmp_on = 0;
+	st->chip_config.quaternion_on = 0;
+
+	return 0;
+}
+
+/**
+ * inv_quaternion_on() -  calling this function will store
+ *                                 current quaternion on
+ */
+static int inv_quaternion_on(struct inv_mpu_iio_s *st,
+				 struct iio_buffer *ring, bool en)
+{
+	unsigned int result;
+	result = inv_send_quaternion(st, en);
+	if (result)
+		return result;
+	st->chip_config.quaternion_on = en;
+	if (!en) {
+		clear_bit(INV_MPU_SCAN_QUAT_R, ring->scan_mask);
+		clear_bit(INV_MPU_SCAN_QUAT_X, ring->scan_mask);
+		clear_bit(INV_MPU_SCAN_QUAT_Y, ring->scan_mask);
+		clear_bit(INV_MPU_SCAN_QUAT_Z, ring->scan_mask);
+	}
+
+	return 0;
+}
+
+/**
+ *  inv_lpa_mode() - store current low power mode settings
+ */
+static int inv_lpa_mode(struct inv_mpu_iio_s *st, int lpa_mode)
+{
+	unsigned long result;
+	u8 d;
+	struct inv_reg_map_s *reg;
+
+	reg = &st->reg;
+	result = inv_i2c_read(st, reg->pwr_mgmt_1, 1, &d);
+	if (result)
+		return result;
+	if (lpa_mode)
+		d |= BIT_CYCLE;
+	else
+		d &= ~BIT_CYCLE;
+
+	result = inv_i2c_single_write(st, reg->pwr_mgmt_1, d);
+	if (result)
+		return result;
+	if (INV_MPU6500 == st->chip_type) {
+		result = inv_i2c_single_write(st, REG_6500_ACCEL_CONFIG2,
+					      BIT_ACCEL_FCHOCIE_B);
+		if (result)
+			return result;
+	}
+	st->chip_config.lpa_mode = !!lpa_mode;
+
+	return 0;
+}
+
+/**
+ *  inv_lpa_freq() - store current low power frequency setting.
+ */
+static int inv_lpa_freq(struct inv_mpu_iio_s *st, int lpa_freq)
+{
+	unsigned long result;
+	u8 d;
+	struct inv_reg_map_s *reg;
+
+	if (INV_MPU6500 == st->chip_type) {
+		if (lpa_freq > MAX_6500_LPA_FREQ_PARAM)
+			return -EINVAL;
+		result = inv_i2c_single_write(st, REG_6500_LP_ACCEL_ODR,
+					      lpa_freq);
+		if (result)
+			return result;
+	} else {
+		if (lpa_freq > MAX_LPA_FREQ_PARAM)
+			return -EINVAL;
+		reg = &st->reg;
+		result = inv_i2c_read(st, reg->pwr_mgmt_2, 1, &d);
+		if (result)
+			return result;
+		d &= ~BIT_LPA_FREQ;
+		d |= (u8)(lpa_freq << LPA_FREQ_SHIFT);
+		result = inv_i2c_single_write(st, reg->pwr_mgmt_2, d);
+		if (result)
+			return result;
+	}
+	st->chip_config.lpa_freq = lpa_freq;
+
+	return 0;
+}
+
+static int inv_switch_engine(struct inv_mpu_iio_s *st, bool en, u32 mask)
+{
+	struct inv_reg_map_s *reg;
+	u8 data;
+	int result;
+	reg = &st->reg;
+	result = inv_i2c_read(st, reg->pwr_mgmt_2, 1, &data);
+	if (result)
+		return result;
+	if (en)
+		data &= (~mask);
+	else
+		data |= mask;
+	result = inv_i2c_single_write(st, reg->pwr_mgmt_2, data);
+	if (result)
+		return result;
+	if (en)
+		msleep(SENSOR_UP_TIME);
+
+	return 0;
+
+}
+static int inv_switch_gyro_engine(struct inv_mpu_iio_s *st, bool en)
+{
+	return inv_switch_engine(st, en, BIT_PWR_GYRO_STBY);
+}
+
+static int inv_switch_accl_engine(struct inv_mpu_iio_s *st, bool en)
+{
+	return inv_switch_engine(st, en, BIT_PWR_ACCL_STBY);
+}
+
+/**
+ *  inv_gyro_enable() - Enable/disable gyro.
+ */
+static int inv_gyro_enable(struct inv_mpu_iio_s *st,
+				 struct iio_buffer *ring, bool en)
+{
+	int result;
+	if (en == st->chip_config.gyro_enable)
+		return 0;
+	result = st->switch_gyro_engine(st, en);
+	if (result)
+		return result;
+	if (en)
+		st->chip_config.clk_src = INV_CLK_PLL;
+	else
+		st->chip_config.clk_src = INV_CLK_INTERNAL;
+
+	if (!en) {
+		st->chip_config.gyro_fifo_enable = 0;
+		clear_bit(INV_MPU_SCAN_GYRO_X, ring->scan_mask);
+		clear_bit(INV_MPU_SCAN_GYRO_Y, ring->scan_mask);
+		clear_bit(INV_MPU_SCAN_GYRO_Z, ring->scan_mask);
+	}
+	st->chip_config.gyro_enable = en;
+
+	return 0;
+}
+
+/**
+ *  inv_accl_enable() - Enable/disable accl.
+ */
+static ssize_t inv_accl_enable(struct inv_mpu_iio_s *st,
+				 struct iio_buffer *ring, bool en)
+{
+	int result;
+	if (en == st->chip_config.accl_enable)
+		return 0;
+	result = st->switch_accl_engine(st, en);
+	if (result)
+		return result;
+	st->chip_config.accl_enable = en;
+	if (!en) {
+		st->chip_config.accl_fifo_enable = 0;
+		clear_bit(INV_MPU_SCAN_ACCL_X, ring->scan_mask);
+		clear_bit(INV_MPU_SCAN_ACCL_Y, ring->scan_mask);
+		clear_bit(INV_MPU_SCAN_ACCL_Z, ring->scan_mask);
+	}
+
+	return 0;
+}
+
+/**
+ * inv_compass_enable() -  calling this function will store compass
+ *                         enable
+ */
+static ssize_t inv_compass_enable(struct inv_mpu_iio_s *st,
+				 struct iio_buffer *ring, bool en)
+{
+	if (en == st->chip_config.compass_enable)
+		return 0;
+	st->chip_config.compass_enable = en;
+	if (!en) {
+		st->chip_config.compass_fifo_enable = 0;
+		clear_bit(INV_MPU_SCAN_MAGN_X, ring->scan_mask);
+		clear_bit(INV_MPU_SCAN_MAGN_Y, ring->scan_mask);
+		clear_bit(INV_MPU_SCAN_MAGN_Z, ring->scan_mask);
+	}
+
+	return 0;
+}
+
+/**
+ * inv_attr_store() -  calling this function will store current
+ *                        non-dmp parameter settings
+ */
+static ssize_t inv_attr_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+	struct iio_buffer *ring = indio_dev->buffer;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int data;
+	int result;
+	if (check_enable(st))
+		return -EPERM;
+	result = kstrtoint(buf, 10, &data);
+	if (result)
+		return -EINVAL;
+
+	switch (this_attr->address) {
+	case ATTR_GYRO_ENABLE:
+		result = inv_gyro_enable(st, ring, !!data);
+		break;
+	case ATTR_ACCL_ENABLE:
+		result = inv_accl_enable(st, ring, !!data);
+		break;
+	case ATTR_COMPASS_ENABLE:
+		result = inv_compass_enable(st, ring, !!data);
+		break;
+	case ATTR_DMP_QUATERNION_ON:
+		if (!st->chip_config.firmware_loaded)
+			return -EPERM;
+		result = inv_quaternion_on(st, ring, !!data);
+		break;
+	case ATTR_LPA_FREQ:
+		result = inv_lpa_freq(st, data);
+		break;
+	case ATTR_LPA_MODE:
+		result = inv_lpa_mode(st, data);
+		break;
+	case ATTR_FIRMWARE_LOADED:
+		result = inv_firmware_loaded(st, data);
+		break;
+	default:
+		return -EINVAL;
+	};
+	if (result)
+		return result;
+
+	return count;
+}
+
+#ifdef CONFIG_INV_TESTING
+/**
+ * inv_reg_write_store() - register write command for testing.
+ *                         Format: WSRRDD, where RR is the register in hex,
+ *                                         and DD is the data in hex.
+ */
+static ssize_t inv_reg_write_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+	unsigned int result;
+	u8 wreg, wval;
+	int temp;
+	char local_buf[10];
+
+	if ((buf[0] != 'W' && buf[0] != 'w') ||
+	    (buf[1] != 'S' && buf[1] != 's'))
+		return -EINVAL;
+	if (strlen(buf) < 6)
+		return -EINVAL;
+
+	strncpy(local_buf, buf, 7);
+	local_buf[6] = 0;
+	result = sscanf(&local_buf[4], "%x", &temp);
+	if (result == 0)
+		return -EINVAL;
+	wval = temp;
+	local_buf[4] = 0;
+	sscanf(&local_buf[2], "%x", &temp);
+	if (result == 0)
+		return -EINVAL;
+	wreg = temp;
+
+	result = inv_i2c_single_write(st, wreg, wval);
+	if (result)
+		return result;
+
+	return count;
+}
+#endif /* CONFIG_INV_TESTING */
+
+#define INV_MPU_CHAN(_type, _channel2, _index)                        \
+	{                                                             \
+		.type = _type,                                        \
+		.modified = 1,                                        \
+		.channel2 = _channel2,                                \
+		.info_mask =  (IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
+				IIO_CHAN_INFO_SCALE_SHARED_BIT),      \
+		.scan_index = _index,                                 \
+		.scan_type  = IIO_ST('s', 16, 16, 0)                  \
+	}
+
+#define INV_MPU_QUATERNION_CHAN(_channel2, _index)                    \
+	{                                                             \
+		.type = IIO_QUATERNION,                               \
+		.modified = 1,                                        \
+		.channel2 = _channel2,                                \
+		.scan_index = _index,                                 \
+		.scan_type  = IIO_ST('s', 32, 32, 0)                  \
+	}
+
+#define INV_MPU_MAGN_CHAN(_channel2, _index)                          \
+	{                                                             \
+		.type = IIO_MAGN,                                     \
+		.modified = 1,                                        \
+		.channel2 = _channel2,                                \
+		.info_mask =  IIO_CHAN_INFO_SCALE_SHARED_BIT,         \
+		.scan_index = _index,                                 \
+		.scan_type  = IIO_ST('s', 16, 16, 0)                  \
+	}
+
+static const struct iio_chan_spec inv_mpu_channels[] = {
+	IIO_CHAN_SOFT_TIMESTAMP(INV_MPU_SCAN_TIMESTAMP),
+
+	INV_MPU_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU_SCAN_GYRO_X),
+	INV_MPU_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU_SCAN_GYRO_Y),
+	INV_MPU_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU_SCAN_GYRO_Z),
+
+	INV_MPU_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU_SCAN_ACCL_X),
+	INV_MPU_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU_SCAN_ACCL_Y),
+	INV_MPU_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU_SCAN_ACCL_Z),
+
+	INV_MPU_QUATERNION_CHAN(IIO_MOD_R, INV_MPU_SCAN_QUAT_R),
+	INV_MPU_QUATERNION_CHAN(IIO_MOD_X, INV_MPU_SCAN_QUAT_X),
+	INV_MPU_QUATERNION_CHAN(IIO_MOD_Y, INV_MPU_SCAN_QUAT_Y),
+	INV_MPU_QUATERNION_CHAN(IIO_MOD_Z, INV_MPU_SCAN_QUAT_Z),
+
+	INV_MPU_MAGN_CHAN(IIO_MOD_X, INV_MPU_SCAN_MAGN_X),
+	INV_MPU_MAGN_CHAN(IIO_MOD_Y, INV_MPU_SCAN_MAGN_Y),
+	INV_MPU_MAGN_CHAN(IIO_MOD_Z, INV_MPU_SCAN_MAGN_Z),
+};
+
+/*constant IIO attribute */
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 20 50 100 200 500");
+static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, inv_fifo_rate_show,
+	inv_fifo_rate_store);
+static DEVICE_ATTR(temperature, S_IRUGO, inv_temperature_show, NULL);
+static IIO_DEVICE_ATTR(clock_source, S_IRUGO, inv_attr_show, NULL,
+	ATTR_CLK_SRC);
+static IIO_DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_power_state_store, ATTR_POWER_STATE);
+static IIO_DEVICE_ATTR(firmware_loaded, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_attr_store, ATTR_FIRMWARE_LOADED);
+static IIO_DEVICE_ATTR(lpa_mode, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_attr_store, ATTR_LPA_MODE);
+static IIO_DEVICE_ATTR(lpa_freq, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_attr_store, ATTR_LPA_FREQ);
+static DEVICE_ATTR(reg_dump, S_IRUGO, inv_reg_dump_show, NULL);
+static IIO_DEVICE_ATTR(self_test, S_IRUGO, inv_attr_show, NULL,
+	ATTR_SELF_TEST);
+static IIO_DEVICE_ATTR(key, S_IRUGO, inv_attr_show, NULL, ATTR_KEY);
+static IIO_DEVICE_ATTR(gyro_matrix, S_IRUGO, inv_attr_show, NULL,
+	ATTR_GYRO_MATRIX);
+static IIO_DEVICE_ATTR(accl_matrix, S_IRUGO, inv_attr_show, NULL,
+	ATTR_ACCL_MATRIX);
+static IIO_DEVICE_ATTR(compass_matrix, S_IRUGO, inv_attr_show, NULL,
+	ATTR_COMPASS_MATRIX);
+static IIO_DEVICE_ATTR(flick_lower, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_FLICK_LOWER);
+static IIO_DEVICE_ATTR(flick_upper, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_FLICK_UPPER);
+static IIO_DEVICE_ATTR(flick_counter, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_FLICK_COUNTER);
+static IIO_DEVICE_ATTR(flick_message_on, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_FLICK_MSG_ON);
+static IIO_DEVICE_ATTR(flick_int_on, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_FLICK_INT_ON);
+static IIO_DEVICE_ATTR(flick_axis, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_FLICK_AXIS);
+static IIO_DEVICE_ATTR(dmp_on, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_ON);
+static IIO_DEVICE_ATTR(dmp_int_on, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_INT_ON);
+static IIO_DEVICE_ATTR(dmp_event_int_on, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_EVENT_INT_ON);
+static IIO_DEVICE_ATTR(dmp_output_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_OUTPUT_RATE);
+static IIO_DEVICE_ATTR(orientation_on, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_ORIENTATION_ON);
+static IIO_DEVICE_ATTR(quaternion_on, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_attr_store, ATTR_DMP_QUATERNION_ON);
+static IIO_DEVICE_ATTR(display_orientation_on, S_IRUGO | S_IWUSR,
+	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_DISPLAY_ORIENTATION_ON);
+static IIO_DEVICE_ATTR(tap_on, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_TAP_ON);
+static IIO_DEVICE_ATTR(tap_time, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_TAP_TIME);
+static IIO_DEVICE_ATTR(tap_min_count, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_TAP_MIN_COUNT);
+static IIO_DEVICE_ATTR(tap_threshold, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_TAP_THRESHOLD);
+static IIO_DEVICE_ATTR(pedometer_time, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_PEDOMETER_TIME);
+static IIO_DEVICE_ATTR(pedometer_steps, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_dmp_attr_store, ATTR_DMP_PEDOMETER_STEPS);
+static DEVICE_ATTR(event_flick, S_IRUGO, inv_dmp_flick_show, NULL);
+static DEVICE_ATTR(event_orientation, S_IRUGO, inv_dmp_orient_show, NULL);
+static DEVICE_ATTR(event_tap, S_IRUGO, inv_dmp_tap_show, NULL);
+static DEVICE_ATTR(event_display_orientation, S_IRUGO,
+	inv_dmp_display_orient_show, NULL);
+static IIO_DEVICE_ATTR(gyro_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_attr_store, ATTR_GYRO_ENABLE);
+static IIO_DEVICE_ATTR(accl_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_attr_store, ATTR_ACCL_ENABLE);
+static IIO_DEVICE_ATTR(compass_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_attr_store, ATTR_COMPASS_ENABLE);
+#ifdef CONFIG_INV_TESTING
+static IIO_DEVICE_ATTR(i2c_counters, S_IRUGO, inv_attr_show, NULL,
+	ATTR_I2C_COUNTERS);
+static IIO_DEVICE_ATTR(reg_write, S_IRUGO | S_IWUSR, inv_attr_show,
+	inv_reg_write_store, ATTR_REG_WRITE);
+#endif
+
+static const struct attribute *inv_gyro_attributes[] = {
+	&iio_dev_attr_gyro_enable.dev_attr.attr,
+	&dev_attr_temperature.attr,
+	&iio_dev_attr_clock_source.dev_attr.attr,
+	&iio_dev_attr_power_state.dev_attr.attr,
+	&dev_attr_reg_dump.attr,
+	&iio_dev_attr_self_test.dev_attr.attr,
+	&iio_dev_attr_key.dev_attr.attr,
+	&iio_dev_attr_gyro_matrix.dev_attr.attr,
+#ifdef CONFIG_INV_TESTING
+	&iio_dev_attr_i2c_counters.dev_attr.attr,
+	&iio_dev_attr_reg_write.dev_attr.attr,
+#endif
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+};
+
+static const struct attribute *inv_mpu6050_attributes[] = {
+	&iio_dev_attr_accl_enable.dev_attr.attr,
+	&iio_dev_attr_accl_matrix.dev_attr.attr,
+	&iio_dev_attr_firmware_loaded.dev_attr.attr,
+	&iio_dev_attr_lpa_mode.dev_attr.attr,
+	&iio_dev_attr_lpa_freq.dev_attr.attr,
+	&iio_dev_attr_flick_lower.dev_attr.attr,
+	&iio_dev_attr_flick_upper.dev_attr.attr,
+	&iio_dev_attr_flick_counter.dev_attr.attr,
+	&iio_dev_attr_flick_message_on.dev_attr.attr,
+	&iio_dev_attr_flick_int_on.dev_attr.attr,
+	&iio_dev_attr_flick_axis.dev_attr.attr,
+	&iio_dev_attr_dmp_on.dev_attr.attr,
+	&iio_dev_attr_dmp_int_on.dev_attr.attr,
+	&iio_dev_attr_dmp_event_int_on.dev_attr.attr,
+	&iio_dev_attr_dmp_output_rate.dev_attr.attr,
+	&iio_dev_attr_orientation_on.dev_attr.attr,
+	&iio_dev_attr_quaternion_on.dev_attr.attr,
+	&iio_dev_attr_display_orientation_on.dev_attr.attr,
+	&iio_dev_attr_tap_on.dev_attr.attr,
+	&iio_dev_attr_tap_time.dev_attr.attr,
+	&iio_dev_attr_tap_min_count.dev_attr.attr,
+	&iio_dev_attr_tap_threshold.dev_attr.attr,
+	&iio_dev_attr_pedometer_time.dev_attr.attr,
+	&iio_dev_attr_pedometer_steps.dev_attr.attr,
+	&dev_attr_event_flick.attr,
+	&dev_attr_event_orientation.attr,
+	&dev_attr_event_display_orientation.attr,
+	&dev_attr_event_tap.attr,
+};
+
+static const struct attribute *inv_compass_attributes[] = {
+	&iio_dev_attr_compass_matrix.dev_attr.attr,
+	&iio_dev_attr_compass_enable.dev_attr.attr,
+};
+
+static const struct attribute *inv_mpu3050_attributes[] = {
+	&iio_dev_attr_accl_matrix.dev_attr.attr,
+	&iio_dev_attr_accl_enable.dev_attr.attr,
+};
+
+static struct attribute *inv_attributes[ARRAY_SIZE(inv_gyro_attributes) +
+				ARRAY_SIZE(inv_mpu6050_attributes) +
+				ARRAY_SIZE(inv_compass_attributes) + 1];
+
+static const struct attribute_group inv_attribute_group = {
+	.name = "mpu",
+	.attrs = inv_attributes
+};
+
+static const struct iio_info mpu_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &mpu_read_raw,
+	.write_raw = &mpu_write_raw,
+	.attrs = &inv_attribute_group,
+};
+
+/**
+ *  inv_setup_compass() - Configure compass.
+ */
+static int inv_setup_compass(struct inv_mpu_iio_s *st)
+{
+	int result;
+	u8 data[4];
+
+	result = inv_i2c_read(st, REG_YGOFFS_TC, 1, data);
+	if (result)
+		return result;
+	data[0] &= ~BIT_I2C_MST_VDDIO;
+	if (st->plat_data.level_shifter)
+		data[0] |= BIT_I2C_MST_VDDIO;
+	/*set up VDDIO register */
+	result = inv_i2c_single_write(st, REG_YGOFFS_TC, data[0]);
+	if (result)
+		return result;
+	/* set to bypass mode */
+	result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+				st->plat_data.int_config | BIT_BYPASS_EN);
+	if (result)
+		return result;
+	/*read secondary i2c ID register */
+	result = inv_secondary_read(REG_AKM_ID, 1, data);
+	if (result)
+		return result;
+	if (data[0] != DATA_AKM_ID)
+		return -ENXIO;
+	/*set AKM to Fuse ROM access mode */
+	result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_FR);
+	if (result)
+		return result;
+	result = inv_secondary_read(REG_AKM_SENSITIVITY, THREE_AXIS,
+					st->chip_info.compass_sens);
+	if (result)
+		return result;
+	/*revert to power down mode */
+	result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PD);
+	if (result)
+		return result;
+	pr_debug("%s senx=%d, seny=%d, senz=%d\n",
+		 st->hw->name,
+		 st->chip_info.compass_sens[0],
+		 st->chip_info.compass_sens[1],
+		 st->chip_info.compass_sens[2]);
+	/*restore to non-bypass mode */
+	result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+			st->plat_data.int_config);
+	if (result)
+		return result;
+
+	/*setup master mode and master clock and ES bit*/
+	result = inv_i2c_single_write(st, REG_I2C_MST_CTRL, BIT_WAIT_FOR_ES);
+	if (result)
+		return result;
+	/* slave 0 is used to read data from compass */
+	/*read mode */
+	result = inv_i2c_single_write(st, REG_I2C_SLV0_ADDR, BIT_I2C_READ|
+		st->plat_data.secondary_i2c_addr);
+	if (result)
+		return result;
+	/* AKM status register address is 2 */
+	result = inv_i2c_single_write(st, REG_I2C_SLV0_REG, REG_AKM_STATUS);
+	if (result)
+		return result;
+	/* slave 0 is enabled at the beginning, read 8 bytes from here */
+	result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, BIT_SLV_EN |
+				NUM_BYTES_COMPASS_SLAVE);
+	if (result)
+		return result;
+	/*slave 1 is used for AKM mode change only*/
+	result = inv_i2c_single_write(st, REG_I2C_SLV1_ADDR,
+		st->plat_data.secondary_i2c_addr);
+	if (result)
+		return result;
+	/* AKM mode register address is 0x0A */
+	result = inv_i2c_single_write(st, REG_I2C_SLV1_REG, REG_AKM_MODE);
+	if (result)
+		return result;
+	/* slave 1 is enabled, byte length is 1 */
+	result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL, BIT_SLV_EN | 1);
+	if (result)
+		return result;
+	/* output data for slave 1 is fixed, single measure mode*/
+	st->compass_scale = 1;
+	if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id) {
+		st->compass_st_upper = AKM8975_ST_Upper;
+		st->compass_st_lower = AKM8975_ST_Lower;
+		data[0] = DATA_AKM_MODE_SM;
+	} else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id) {
+		st->compass_st_upper = AKM8972_ST_Upper;
+		st->compass_st_lower = AKM8972_ST_Lower;
+		data[0] = DATA_AKM_MODE_SM;
+	} else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) {
+		st->compass_st_upper = AKM8963_ST_Upper;
+		st->compass_st_lower = AKM8963_ST_Lower;
+		data[0] = DATA_AKM_MODE_SM |
+			  (st->compass_scale << AKM8963_SCALE_SHIFT);
+	}
+	result = inv_i2c_single_write(st, REG_I2C_SLV1_DO, data[0]);
+	if (result)
+		return result;
+	/* slave 0 and 1 timer action is enabled every sample*/
+	result = inv_i2c_single_write(st, REG_I2C_MST_DELAY_CTRL,
+				BIT_SLV0_DLY_EN | BIT_SLV1_DLY_EN);
+	return result;
+}
+
+static void inv_setup_func_ptr(struct inv_mpu_iio_s *st)
+{
+	if (st->chip_type == INV_MPU3050) {
+		st->set_power_state    = set_power_mpu3050;
+		st->switch_gyro_engine = inv_switch_3050_gyro_engine;
+		st->switch_accl_engine = inv_switch_3050_accl_engine;
+		st->init_config        = inv_init_config_mpu3050;
+		st->setup_reg          = inv_setup_reg_mpu3050;
+	} else {
+		st->set_power_state    = set_power_itg;
+		st->switch_gyro_engine = inv_switch_gyro_engine;
+		st->switch_accl_engine = inv_switch_accl_engine;
+		st->init_config        = inv_init_config;
+		st->setup_reg          = inv_setup_reg;
+	}
+}
+
+/**
+ *  inv_check_chip_type() - check and setup chip type.
+ */
+static int inv_check_chip_type(struct inv_mpu_iio_s *st,
+		const struct i2c_device_id *id)
+{
+	struct inv_reg_map_s *reg;
+	int result;
+	int t_ind;
+	if (!strcmp(id->name, "itg3500"))
+		st->chip_type = INV_ITG3500;
+	else if (!strcmp(id->name, "mpu3050"))
+		st->chip_type = INV_MPU3050;
+	else if (!strcmp(id->name, "mpu6050"))
+		st->chip_type = INV_MPU6050;
+	else if (!strcmp(id->name, "mpu9150"))
+		st->chip_type = INV_MPU9150;
+	else if (!strcmp(id->name, "mpu6500"))
+		st->chip_type = INV_MPU6500;
+	else
+		return -EPERM;
+	inv_setup_func_ptr(st);
+	st->hw  = &hw_info[st->chip_type];
+	st->mpu_slave = NULL;
+	reg = &st->reg;
+	st->setup_reg(reg);
+	st->chip_config.gyro_enable = 1;
+	/* reset to make sure previous state are not there */
+	result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_H_RESET);
+	if (result)
+		return result;
+	msleep(POWER_UP_TIME);
+	/* turn off and turn on power to ensure gyro engine is on */
+	result = st->set_power_state(st, false);
+	if (result)
+		return result;
+	result = st->set_power_state(st, true);
+	if (result)
+		return result;
+
+	switch (st->chip_type) {
+	case INV_ITG3500:
+		st->num_channels = INV_CHANNEL_NUM_GYRO;
+		break;
+	case INV_MPU6050:
+	case INV_MPU6500:
+		if (SECONDARY_SLAVE_TYPE_COMPASS ==
+		    st->plat_data.sec_slave_type) {
+			st->chip_config.has_compass = 1;
+			st->num_channels =
+				INV_CHANNEL_NUM_GYRO_ACCL_QUANTERNION_MAGN;
+		} else {
+			st->chip_config.has_compass = 0;
+			st->num_channels =
+				INV_CHANNEL_NUM_GYRO_ACCL_QUANTERNION;
+		}
+		break;
+	case INV_MPU9150:
+		st->plat_data.sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS;
+		st->plat_data.sec_slave_id = COMPASS_ID_AK8975;
+		st->chip_config.has_compass = 1;
+		st->num_channels = INV_CHANNEL_NUM_GYRO_ACCL_QUANTERNION_MAGN;
+		break;
+	case INV_MPU3050:
+		if (SECONDARY_SLAVE_TYPE_ACCEL ==
+		    st->plat_data.sec_slave_type) {
+			if (ACCEL_ID_BMA250 == st->plat_data.sec_slave_id)
+				inv_register_mpu3050_slave(st);
+			st->num_channels = INV_CHANNEL_NUM_GYRO_ACCL;
+		} else {
+			st->num_channels = INV_CHANNEL_NUM_GYRO;
+		}
+		break;
+	default:
+		result = st->set_power_state(st, false);
+		return -ENODEV;
+	}
+
+	switch (st->chip_type) {
+	case INV_MPU6050:
+	case INV_MPU9150:
+		result = inv_get_silicon_rev_mpu6050(st);
+		break;
+	case INV_MPU6500:
+		result = inv_get_silicon_rev_mpu6500(st);
+		break;
+	default:
+		result = 0;
+		break;
+	}
+	if (result) {
+		pr_err("read silicon rev error\n");
+		st->set_power_state(st, false);
+		return result;
+	}
+	if (st->chip_config.has_compass) {
+		result = inv_setup_compass(st);
+		if (result) {
+			pr_err("compass setup failed\n");
+			st->set_power_state(st, false);
+			return result;
+		}
+	}
+
+	t_ind = 0;
+	memcpy(&inv_attributes[t_ind], inv_gyro_attributes,
+		sizeof(inv_gyro_attributes));
+	t_ind += ARRAY_SIZE(inv_gyro_attributes);
+
+	if (INV_MPU3050 == st->chip_type && st->mpu_slave != NULL) {
+		memcpy(&inv_attributes[t_ind], inv_mpu3050_attributes,
+		       sizeof(inv_mpu3050_attributes));
+		t_ind += ARRAY_SIZE(inv_mpu3050_attributes);
+		inv_attributes[t_ind] = NULL;
+		return 0;
+	}
+
+	if ((INV_MPU6050 == st->chip_type) ||
+	    (INV_MPU9150 == st->chip_type) ||
+	    (INV_MPU6500 == st->chip_type)) {
+		memcpy(&inv_attributes[t_ind], inv_mpu6050_attributes,
+		       sizeof(inv_mpu6050_attributes));
+		t_ind += ARRAY_SIZE(inv_mpu6050_attributes);
+	}
+
+	if (st->chip_config.has_compass) {
+		memcpy(&inv_attributes[t_ind], inv_compass_attributes,
+		       sizeof(inv_compass_attributes));
+		t_ind += ARRAY_SIZE(inv_compass_attributes);
+	}
+	inv_attributes[t_ind] = NULL;
+
+	return 0;
+}
+
+/**
+ *  inv_create_dmp_sysfs() - create binary sysfs dmp entry.
+ */
+static const struct bin_attribute dmp_firmware = {
+	.attr = {
+		.name = "dmp_firmware",
+		.mode = S_IRUGO | S_IWUSR
+	},
+	.size = 4096,
+	.read = inv_dmp_firmware_read,
+	.write = inv_dmp_firmware_write,
+};
+
+static int inv_create_dmp_sysfs(struct iio_dev *ind)
+{
+	int result;
+	result = sysfs_create_bin_file(&ind->dev.kobj, &dmp_firmware);
+
+	return result;
+}
+
+/**
+ *  inv_mpu_probe() - probe function.
+ */
+static int inv_mpu_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	struct inv_mpu_iio_s *st;
+	struct iio_dev *indio_dev;
+	int result;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		result = -ENOSYS;
+		pr_err("I2c function error\n");
+		goto out_no_free;
+	}
+	indio_dev = iio_allocate_device(sizeof(*st));
+	if (indio_dev == NULL) {
+		pr_err("memory allocation failed\n");
+		result =  -ENOMEM;
+		goto out_no_free;
+	}
+	st = iio_priv(indio_dev);
+	st->client = client;
+	st->sl_handle = client->adapter;
+	st->i2c_addr = client->addr;
+	st->plat_data =
+		*(struct mpu_platform_data *)dev_get_platdata(&client->dev);
+	/* power is turned on inside check chip type*/
+	result = inv_check_chip_type(st, id);
+	if (result)
+		goto out_free;
+
+	result = st->init_config(indio_dev);
+	if (result) {
+		dev_err(&client->adapter->dev,
+			"Could not initialize device.\n");
+		goto out_free;
+	}
+	result = st->set_power_state(st, false);
+	if (result) {
+		dev_err(&client->adapter->dev,
+			"%s could not be turned off.\n", st->hw->name);
+		goto out_free;
+	}
+
+	/* Make state variables available to all _show and _store functions. */
+	i2c_set_clientdata(client, indio_dev);
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->name = id->name;
+	indio_dev->channels = inv_mpu_channels;
+	indio_dev->num_channels = st->num_channels;
+
+	indio_dev->info = &mpu_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->currentmode = INDIO_DIRECT_MODE;
+
+	result = inv_mpu_configure_ring(indio_dev);
+	if (result) {
+		pr_err("configure ring buffer fail\n");
+		goto out_free;
+	}
+	result = iio_buffer_register(indio_dev, indio_dev->channels,
+					indio_dev->num_channels);
+	if (result) {
+		pr_err("ring buffer register fail\n");
+		goto out_unreg_ring;
+	}
+	st->irq = client->irq;
+	result = inv_mpu_probe_trigger(indio_dev);
+	if (result) {
+		pr_err("trigger probe fail\n");
+		goto out_remove_ring;
+	}
+
+	result = iio_device_register(indio_dev);
+	if (result) {
+		pr_err("IIO device register fail\n");
+		goto out_remove_trigger;
+	}
+
+	if (INV_MPU6050 == st->chip_type || INV_MPU9150 == st->chip_type ||
+	    INV_MPU6500 == st->chip_type) {
+		result = inv_create_dmp_sysfs(indio_dev);
+		if (result) {
+			pr_err("create dmp sysfs failed\n");
+			goto out_unreg_iio;
+		}
+	}
+
+	INIT_KFIFO(st->timestamps);
+	spin_lock_init(&st->time_stamp_lock);
+	dev_info(&client->adapter->dev, "%s is ready to go!\n", st->hw->name);
+
+	return 0;
+out_unreg_iio:
+	iio_device_unregister(indio_dev);
+out_remove_trigger:
+	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+		inv_mpu_remove_trigger(indio_dev);
+out_remove_ring:
+	iio_buffer_unregister(indio_dev);
+out_unreg_ring:
+	inv_mpu_unconfigure_ring(indio_dev);
+out_free:
+	iio_free_device(indio_dev);
+out_no_free:
+	dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+
+	return -EIO;
+}
+
+static void inv_mpu_shutdown(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+	struct inv_reg_map_s *reg;
+	int result;
+
+	reg = &st->reg;
+
+	dev_dbg(&client->adapter->dev, "Shutting down %s...\n", st->hw->name);
+
+	/* reset to make sure previous state are not there */
+	result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_H_RESET);
+	if (result)
+		dev_err(&client->adapter->dev, "Failed to reset %s\n", st->hw->name);
+	msleep(POWER_UP_TIME);
+	/* turn off power to ensure gyro engine is off */
+	result = st->set_power_state(st, false);
+	if (result)
+		dev_err(&client->adapter->dev, "Failed to turn off %s\n", st->hw->name);
+}
+
+/**
+ *  inv_mpu_remove() - remove function.
+ */
+static int inv_mpu_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+	kfifo_free(&st->timestamps);
+	iio_device_unregister(indio_dev);
+	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+		inv_mpu_remove_trigger(indio_dev);
+	iio_buffer_unregister(indio_dev);
+	inv_mpu_unconfigure_ring(indio_dev);
+	iio_free_device(indio_dev);
+
+	dev_info(&client->adapter->dev, "inv-mpu-iio module removed.\n");
+
+	return 0;
+}
+#ifdef CONFIG_PM
+
+static int inv_mpu_resume(struct device *dev)
+{
+	struct inv_mpu_iio_s *st =
+			iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+	if (st->chip_config.was_asleep_on_suspend)
+		return 0;
+	return st->set_power_state(st, true);
+}
+
+static int inv_mpu_suspend(struct device *dev)
+{
+	struct inv_mpu_iio_s *st =
+			iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+	st->chip_config.was_asleep_on_suspend = st->chip_config.is_asleep;
+	return st->set_power_state(st, false);
+}
+static const struct dev_pm_ops inv_mpu_pmops = {
+	SET_SYSTEM_SLEEP_PM_OPS(inv_mpu_suspend, inv_mpu_resume)
+};
+#define INV_MPU_PMOPS (&inv_mpu_pmops)
+#else
+#define INV_MPU_PMOPS NULL
+#endif /* CONFIG_PM */
+
+static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
+/* device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id inv_mpu_id[] = {
+	{"itg3500", INV_ITG3500},
+	{"mpu3050", INV_MPU3050},
+	{"mpu6050", INV_MPU6050},
+	{"mpu9150", INV_MPU9150},
+	{"mpu6500", INV_MPU6500},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
+
+static struct i2c_driver inv_mpu_driver = {
+	.class = I2C_CLASS_HWMON,
+	.probe		=	inv_mpu_probe,
+	.remove		=	inv_mpu_remove,
+	.shutdown	=	inv_mpu_shutdown,
+	.id_table	=	inv_mpu_id,
+	.driver = {
+		.owner	=	THIS_MODULE,
+		.name	=	"inv-mpu-iio",
+		.pm     =       INV_MPU_PMOPS,
+	},
+	.address_list = normal_i2c,
+};
+
+static int __init inv_mpu_init(void)
+{
+	int result = i2c_add_driver(&inv_mpu_driver);
+	if (result) {
+		pr_err("failed\n");
+		return result;
+	}
+	return 0;
+}
+
+static void __exit inv_mpu_exit(void)
+{
+	i2c_del_driver(&inv_mpu_driver);
+}
+
+module_init(inv_mpu_init);
+module_exit(inv_mpu_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("inv-mpu-iio");
+/**
+ *  @}
+ */
diff --git a/drivers/staging/iio/imu/mpu/inv_mpu_iio.h b/drivers/staging/iio/imu/mpu/inv_mpu_iio.h
new file mode 100644
index 0000000..50169f8
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu/inv_mpu_iio.h
@@ -0,0 +1,809 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup DRIVERS
+ *  @brief      Hardware drivers.
+ *
+ *  @{
+ *      @file  inv_mpu_iio.h
+ *      @brief Struct definitions for the Invensense mpu driver.
+ */
+
+#ifndef _INV_MPU_IIO_H_
+#define _INV_MPU_IIO_H_
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/miscdevice.h>
+#include <linux/input.h>
+#include <linux/spinlock.h>
+#include <linux/mpu.h>
+#include "../../iio.h"
+#include "../../buffer.h"
+#include "dmpKey.h"
+/**
+ *  struct inv_reg_map_s - Notable slave registers.
+ *  @sample_rate_div:	Divider applied to gyro output rate.
+ *  @lpf:		Configures internal LPF.
+ *  @bank_sel:		Selects between memory banks.
+ *  @user_ctrl:		Enables/resets the FIFO.
+ *  @fifo_en:		Determines which data will appear in FIFO.
+ *  @gyro_config:	gyro config register.
+ *  @accl_config:	accel config register
+ *  @fifo_count_h:	Upper byte of FIFO count.
+ *  @fifo_r_w:		FIFO register.
+ *  @raw_gyro		Address of first gyro register.
+ *  @raw_accl		Address of first accel register.
+ *  @temperature	temperature register
+ *  @int_enable:	Interrupt enable register.
+ *  @int_status:	Interrupt flags.
+ *  @pwr_mgmt_1:	Controls chip's power state and clock source.
+ *  @pwr_mgmt_2:	Controls power state of individual sensors.
+ *  @mem_start_addr:	Address of first memory read.
+ *  @mem_r_w:		Access to memory.
+ *  @prgm_strt_addrh	firmware program start address register
+ */
+struct inv_reg_map_s {
+	u8 sample_rate_div;
+	u8 lpf;
+	u8 bank_sel;
+	u8 user_ctrl;
+	u8 fifo_en;
+	u8 gyro_config;
+	u8 accl_config;
+	u8 fifo_count_h;
+	u8 fifo_r_w;
+	u8 raw_gyro;
+	u8 raw_accl;
+	u8 temperature;
+	u8 int_enable;
+	u8 int_status;
+	u8 pwr_mgmt_1;
+	u8 pwr_mgmt_2;
+	u8 mem_start_addr;
+	u8 mem_r_w;
+	u8 prgm_strt_addrh;
+};
+/*device enum */
+enum inv_devices {
+	INV_ITG3500,
+	INV_MPU3050,
+	INV_MPU6050,
+	INV_MPU9150,
+	INV_MPU6500,
+	INV_NUM_PARTS
+};
+
+/**
+ *  struct test_setup_t - set up parameters for self test.
+ *  @gyro_sens: sensitity for gyro.
+ *  @sample_rate: sample rate, i.e, fifo rate.
+ *  @lpf:	low pass filter.
+ *  @fsr:	full scale range.
+ *  @accl_fs:	accel full scale range.
+ *  @accl_sens:	accel sensitivity
+ */
+struct test_setup_t {
+	int gyro_sens;
+	int sample_rate;
+	int lpf;
+	int fsr;
+	int accl_fs;
+	u32 accl_sens[3];
+};
+
+/**
+ *  struct inv_hw_s - Other important hardware information.
+ *  @num_reg:	Number of registers on device.
+ *  @name:      name of the chip
+ */
+struct inv_hw_s {
+	u8 num_reg;
+	u8 *name;
+};
+
+/**
+ *  struct inv_chip_config_s - Cached chip configuration data.
+ *  @fsr:		Full scale range.
+ *  @lpf:		Digital low pass filter frequency.
+ *  @clk_src:		Clock source.
+ *  @accl_fs:		accel full scale range.
+ *  @self_test_run_once flag for self test run ever.
+ *  @has_footer:	MPU3050 specific work around.
+ *  @has_compass:	has compass or not.
+ *  @enable:		master enable to enable output
+ *  @accl_enable:	enable accel functionality
+ *  @accl_fifo_enable:	enable accel data output
+ *  @gyro_enable:	enable gyro functionality
+ *  @gyro_fifo_enable:	enable gyro data output
+ *  @compass_enable:	enable compass
+ *  @compass_fifo_enable: enable compass data output
+ *  @is_asleep:		1 if chip is powered down.
+ *  @was_asleep_on_suspend:	1 if chip was powered at time of sustpend.
+ *  @dmp_on:		dmp is on/off.
+ *  @dmp_int_on:        dmp interrupt on/off.
+ *  @dmp_event_int_on:  dmp event interrupt on/off.
+ *  @orientation_on:	dmp is on/off.
+ *  @firmware_loaded:	flag indicate firmware loaded or not.
+ *  @lpa_mod:		low power mode.
+ *  @tap_on:		tap on/off.
+ *  @flick_int_on:	flick interrupt on/off.
+ *  @quaternion_on:	send quaternion data on/off.
+ *  @display_orient_on:	display orientation on/off.
+ *  @lpa_freq:		low power frequency
+ *  @prog_start_addr:	firmware program start address.
+ *  @dmp_output_rate:   dmp output rate.
+ *  @fifo_rate:		FIFO update rate.
+ */
+struct inv_chip_config_s {
+	u32 fsr:2;
+	u32 lpf:3;
+	u32 clk_src:1;
+	u32 accl_fs:2;
+	u32 self_test_run_once:1;
+	u32 has_footer:1;
+	u32 has_compass:1;
+	u32 enable:1;
+	u32 accl_enable:1;
+	u32 accl_fifo_enable:1;
+	u32 gyro_enable:1;
+	u32 gyro_fifo_enable:1;
+	u32 compass_enable:1;
+	u32 compass_fifo_enable:1;
+	u32 is_asleep:1;
+	u32 was_asleep_on_suspend:1;
+	u32 dmp_on:1;
+	u32 dmp_int_on:1;
+	u32 dmp_event_int_on:1;
+	u32 orientation_on:1;
+	u32 firmware_loaded:1;
+	u32 lpa_mode:1;
+	u32 tap_on:1;
+	u32 flick_int_on:1;
+	u32 quaternion_on:1;
+	u32 display_orient_on:1;
+	u16 lpa_freq;
+	u16  prog_start_addr;
+	u16 fifo_rate;
+	u16 dmp_output_rate;
+};
+
+/**
+ *  struct inv_chip_info_s - Chip related information.
+ *  @product_id:	Product id.
+ *  @product_revision:	Product revision.
+ *  @silicon_revision:	Silicon revision.
+ *  @software_revision:	software revision.
+ *  @multi:		accel specific multiplier.
+ *  @compass_sens:	compass sensitivity.
+ *  @gyro_sens_trim:	Gyro sensitivity trim factor.
+ *  @accl_sens_trim:    accel sensitivity trim factor.
+ */
+struct inv_chip_info_s {
+	u8 product_id;
+	u8 product_revision;
+	u8 silicon_revision;
+	u8 software_revision;
+	u8 multi;
+	u8 compass_sens[3];
+	unsigned long gyro_sens_trim;
+	unsigned long accl_sens_trim;
+};
+
+/**
+ *  struct inv_flick_s structure to store flick data.
+ *  @lower:	lower bound of flick.
+ *  @upper:     upper bound of flick.
+ *  @counter:	counter of flick.
+ *  @msg_on;    message to carry flick
+ *  @axis:      axis of flick
+ */
+struct inv_flick_s {
+	int lower;
+	int upper;
+	int counter;
+	int msg_on;
+	int axis;
+};
+
+enum inv_channel_num {
+	INV_CHANNEL_NUM_GYRO = 4,
+	INV_CHANNEL_NUM_GYRO_ACCL = 7,
+	INV_CHANNEL_NUM_GYRO_ACCL_QUANTERNION = 11,
+	INV_CHANNEL_NUM_GYRO_ACCL_QUANTERNION_MAGN = 14,
+};
+
+/**
+ *  struct inv_tap_s structure to store tap data.
+ *  @min_count:  minimum taps counted.
+ *  @thresh:    tap threshold.
+ *  @time:	tap time.
+ */
+struct inv_tap_s {
+	u16 min_count;
+	u16 thresh;
+	u16 time;
+};
+struct inv_mpu_slave;
+/**
+ *  struct inv_mpu_iio_s - Driver state variables.
+ *  @chip_config:	Cached attribute information.
+ *  @chip_info:		Chip information from read-only registers.
+ *  @trig;              iio trigger.
+ *  @flick:		flick data structure
+ *  @tap:               tap data structure
+ *  @reg:		Map of important registers.
+ *  @hw:		Other hardware-specific information.
+ *  @chip_type:		chip type.
+ *  @time_stamp_lock:	spin lock to time stamp.
+ *  @client:		i2c client handle.
+ *  @plat_data:		platform data.
+ *  @mpu_slave:		mpu slave handle.
+ *  int (*set_power_state)(struct inv_mpu_iio_s *, int on): function ptr
+ *  int (*switch_gyro_engine)(struct inv_mpu_iio_s *, int on): function ptr
+ *  int (*switch_accl_engine)(struct inv_mpu_iio_s *, int on): function ptr
+ *  int (*init_config)(struct iio_dev *indio_dev): function ptr
+ * void (*setup_reg)(struct inv_reg_map_s *reg): function ptr
+ *  @timestamps:        kfifo queue to store time stamp.
+ *  @compass_st_upper:  compass self test upper limit.
+ *  @compass_st_lower:  compass self test lower limit.
+ *  @irq:               irq number store.
+ *  @accel_bias:        accel bias store.
+ *  @gyro_bias:         gyro bias store.
+ *  @raw_gyro:          raw gyro data.
+ *  @raw_accel:         raw accel data.
+ *  @raw_compass:       raw compass.
+ *  @raw_quaternion     raw quaternion data.
+ *  @compass_scale:     compass scale.
+ *  @i2c_addr:          i2c address.
+ *  @compass_divider:   slow down compass rate.
+ *  @compass_dmp_divider: slow down compass rate for dmp.
+ *  @compass_counter:   slow down compass rate.
+ *  @sample_divider:    sample divider for dmp.
+ *  @fifo_divider:      fifo divider for dmp.
+ *  @orient_data:       orientation data.
+ *  @display_orient_data:display orient data.
+ *  @tap_data:          tap data.
+ *  @num_channels:      number of channels for current chip.
+ *  @sl_handle:         Handle to I2C port.
+ *  @irq_dur_ns:        duration between each irq.
+ *  @last_isr_time:     last isr time.
+ */
+struct inv_mpu_iio_s {
+#define TIMESTAMP_FIFO_SIZE 16
+	struct inv_chip_config_s chip_config;
+	struct inv_chip_info_s chip_info;
+	struct iio_trigger  *trig;
+	struct inv_flick_s flick;
+	struct inv_tap_s   tap;
+	struct inv_reg_map_s reg;
+	const struct inv_hw_s *hw;
+	enum   inv_devices chip_type;
+	spinlock_t time_stamp_lock;
+	struct i2c_client *client;
+	struct mpu_platform_data plat_data;
+	struct inv_mpu_slave *mpu_slave;
+	int (*set_power_state)(struct inv_mpu_iio_s *, bool on);
+	int (*switch_gyro_engine)(struct inv_mpu_iio_s *, bool on);
+	int (*switch_accl_engine)(struct inv_mpu_iio_s *, bool on);
+	int (*init_config)(struct iio_dev *indio_dev);
+	void (*setup_reg)(struct inv_reg_map_s *reg);
+	DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
+	const short *compass_st_upper;
+	const short *compass_st_lower;
+	short irq;
+	int accel_bias[3];
+	int gyro_bias[3];
+	short raw_gyro[3];
+	short raw_accel[3];
+	short raw_compass[3];
+	int raw_quaternion[4];
+	u8 compass_scale;
+	u8 i2c_addr;
+	u8 compass_divider;
+	u8 compass_counter;
+	u8 compass_dmp_divider;
+	u8 sample_divider;
+	u8 fifo_divider;
+	u8 orient_data;
+	u8 display_orient_data;
+	u8 tap_data;
+	enum inv_channel_num num_channels;
+	void *sl_handle;
+	u32 irq_dur_ns;
+	u64 last_isr_time;
+#ifdef CONFIG_INV_TESTING
+	unsigned long i2c_readcount;
+	unsigned long i2c_writecount;
+#endif
+};
+
+/* produces an unique identifier for each device based on the
+   combination of product version and product revision */
+struct prod_rev_map_t {
+	u16 mpl_product_key;
+	u8 silicon_rev;
+	u16 gyro_trim;
+	u16 accel_trim;
+};
+
+/**
+ *  struct inv_mpu_slave - MPU slave structure.
+ *  @suspend:		suspend operation.
+ *  @resume:		resume operation.
+ *  @setup:		setup chip. initialization.
+ *  @combine_data:	combine raw data into meaningful data.
+ *  @get_mode:		get current chip mode.
+ *  @set_lpf            set low pass filter.
+ *  @set_fs             set full scale
+ */
+struct inv_mpu_slave {
+	int (*suspend)(struct inv_mpu_iio_s *);
+	int (*resume)(struct inv_mpu_iio_s *);
+	int (*setup)(struct inv_mpu_iio_s *);
+	int (*combine_data)(u8 *in, short *out);
+	int (*get_mode)(void);
+	int (*set_lpf)(struct inv_mpu_iio_s *, int rate);
+	int (*set_fs)(struct inv_mpu_iio_s *, int fs);
+};
+
+/* AKM definitions */
+#define REG_AKM_ID               0x00
+#define REG_AKM_STATUS           0x02
+#define REG_AKM_MEASURE_DATA     0x03
+#define REG_AKM_MODE             0x0A
+#define REG_AKM_ST_CTRL          0x0C
+#define REG_AKM_SENSITIVITY      0x10
+#define REG_AKM8963_CNTL1        0x0A
+
+#define DATA_AKM_ID              0x48
+#define DATA_AKM_MODE_PD	 0x00
+#define DATA_AKM_MODE_SM	 0x01
+#define DATA_AKM_MODE_ST	 0x08
+#define DATA_AKM_MODE_FR	 0x0F
+#define DATA_AKM_SELF_TEST       0x40
+#define DATA_AKM_DRDY            0x01
+#define DATA_AKM8963_BIT         0x10
+#define DATA_AKM_STAT_MASK       0x0C
+
+#define DATA_AKM8975_SCALE       (9830 * (1L << 15))
+#define DATA_AKM8972_SCALE       (19661 * (1L << 15))
+#define DATA_AKM8963_SCALE0      (19661 * (1L << 15))
+#define DATA_AKM8963_SCALE1      (4915 * (1L << 15))
+#define AKM8963_SCALE_SHIFT      4
+#define NUM_BYTES_COMPASS_SLAVE  8
+
+/*register and associated bit definition*/
+#define REG_3050_FIFO_EN         0x12
+#define BITS_3050_ACCL_OUT		0x0E
+
+#define REG_3050_AUX_VDDIO       0x13
+#define BIT_3050_VDDIO			0x04
+
+#define REG_3050_SLAVE_ADDR      0x14
+#define REG_3050_SAMPLE_RATE_DIV 0x15
+#define REG_3050_LPF             0x16
+#define REG_3050_INT_ENABLE      0x17
+#define REG_3050_AUX_BST_ADDR    0x18
+#define REG_3050_INT_STATUS      0x1A
+#define REG_3050_TEMPERATURE     0x1B
+#define REG_3050_RAW_GYRO        0x1D
+#define REG_3050_AUX_XOUT_H      0x23
+#define REG_3050_FIFO_COUNT_H    0x3A
+#define REG_3050_FIFO_R_W        0x3C
+
+#define REG_3050_USER_CTRL       0x3D
+#define BIT_3050_AUX_IF_EN		0x20
+#define BIT_3050_FIFO_RST		0x02
+
+#define REG_3050_PWR_MGMT_1      0x3E
+#define BITS_3050_POWER1		0x30
+#define BITS_3050_POWER2		0x10
+#define BITS_3050_GYRO_STANDBY		0x38
+
+#define REG_3500_OTP            0x0
+
+#define REG_YGOFFS_TC           0x1
+#define BIT_I2C_MST_VDDIO		0x80
+
+#define REG_XA_OFFS_L_TC        0x7
+#define REG_PRODUCT_ID          0xC
+#define REG_ST_GCT_X            0xD
+#define REG_SAMPLE_RATE_DIV     0x19
+#define REG_CONFIG              0x1A
+
+#define REG_GYRO_CONFIG         0x1B
+#define BITS_SELF_TEST_EN		0xE0
+
+#define REG_ACCEL_CONFIG	0x1C
+
+#define REG_FIFO_EN             0x23
+#define BIT_ACCEL_OUT			0x08
+#define BITS_GYRO_OUT			0x70
+
+
+#define REG_I2C_MST_CTRL        0x24
+#define BIT_WAIT_FOR_ES			0x40
+
+#define REG_I2C_SLV0_ADDR       0x25
+#define BIT_I2C_READ			0x80
+
+#define REG_I2C_SLV0_REG        0x26
+
+#define REG_I2C_SLV0_CTRL       0x27
+#define BIT_SLV_EN			0x80
+
+#define REG_I2C_SLV1_ADDR       0x28
+#define REG_I2C_SLV1_REG        0x29
+#define REG_I2C_SLV1_CTRL       0x2A
+#define REG_I2C_SLV4_CTRL       0x34
+
+#define REG_INT_PIN_CFG         0x37
+#define BIT_BYPASS_EN                   0x2
+
+#define REG_INT_ENABLE          0x38
+#define BIT_DATA_RDY_EN                 0x01
+#define BIT_DMP_INT_EN                  0x02
+
+#define REG_DMP_INT_STATUS      0x39
+#define REG_INT_STATUS          0x3A
+#define REG_RAW_ACCEL           0x3B
+#define REG_TEMPERATURE         0x41
+#define REG_RAW_GYRO            0x43
+#define REG_EXT_SENS_DATA_00    0x49
+#define REG_I2C_SLV1_DO         0x64
+
+#define REG_I2C_MST_DELAY_CTRL  0x67
+#define BIT_SLV0_DLY_EN                 0x01
+#define BIT_SLV1_DLY_EN                 0x02
+
+#define REG_USER_CTRL           0x6A
+#define BIT_FIFO_RST                    0x04
+#define BIT_DMP_RST                     0x08
+#define BIT_I2C_MST_EN                  0x20
+#define BIT_FIFO_EN                     0x40
+#define BIT_DMP_EN                      0x80
+
+#define REG_PWR_MGMT_1          0x6B
+#define BIT_H_RESET                     0x80
+#define BIT_SLEEP                       0x40
+#define BIT_CYCLE                       0x20
+
+#define REG_PWR_MGMT_2          0x6C
+#define BIT_PWR_ACCL_STBY               0x38
+#define BIT_PWR_GYRO_STBY               0x07
+#define BIT_LPA_FREQ                    0xC0
+
+#define REG_BANK_SEL            0x6D
+#define REG_MEM_START_ADDR      0x6E
+#define REG_MEM_RW              0x6F
+#define REG_PRGM_STRT_ADDRH     0x70
+#define REG_FIFO_COUNT_H        0x72
+#define REG_FIFO_R_W            0x74
+#define REG_WHOAMI              0x75
+
+#define REG_6500_ACCEL_CONFIG2  0x1D
+#define BIT_ACCEL_FCHOCIE_B              0x08
+
+#define REG_6500_LP_ACCEL_ODR   0x1E
+
+/* data definitions */
+#define DMP_START_ADDR           0x400
+#define DMP_MASK_TAP             0x3f
+#define DMP_MASK_DIS_ORIEN       0xC0
+#define DMP_DIS_ORIEN_SHIFT      6
+
+#define BYTES_FOR_DMP            16
+#define QUATERNION_BYTES         16
+#define BYTES_PER_SENSOR         6
+#define MPU3050_FOOTER_SIZE      2
+#define FIFO_COUNT_BYTE          2
+#define FIFO_THRESHOLD           500
+#define POWER_UP_TIME            100
+#define SENSOR_UP_TIME           30
+#define MPU_MEM_BANK_SIZE        256
+#define MPU3050_TEMP_OFFSET	 5383314L
+#define MPU3050_TEMP_SCALE       3834792L
+#define MPU6050_TEMP_OFFSET	 2462307L
+#define MPU6050_TEMP_SCALE       2977653L
+#define MPU_TEMP_SHIFT           16
+#define LPA_FREQ_SHIFT           6
+#define COMPASS_RATE_SCALE       10
+#define MAX_GYRO_FS_PARAM        3
+#define MAX_ACCL_FS_PARAM        3
+#define MAX_LPA_FREQ_PARAM       3
+
+/*---- MPU6500 ----*/
+#define MAX_6500_LPA_FREQ_PARAM  11
+#define MPU6500_ID               0x70      /* unique WHOAMI */
+#define MPU6500_PRODUCT_REVISION 1
+
+/*---- MPU9250 ----*/
+#define MPU9250_ID               0x71      /* unique WHOAMI */
+
+#define THREE_AXIS               3
+#define GYRO_CONFIG_FSR_SHIFT    3
+#define ACCL_CONFIG_FSR_SHIFT    3
+#define GYRO_DPS_SCALE           250
+#define MEM_ADDR_PROD_REV        0x6
+#define SOFT_PROD_VER_BYTES      5
+#define CRC_FIRMWARE_SEED        0
+#define SELF_TEST_SUCCESS        1
+#define MS_PER_DMP_TICK          20
+
+/* init parameters */
+#define INIT_FIFO_RATE           50
+#define INIT_DMP_OUTPUT_RATE     25
+#define INIT_DUR_TIME           ((1000 / INIT_FIFO_RATE) * 1000 * 1000)
+#define INIT_TAP_THRESHOLD       100
+#define INIT_TAP_TIME            100
+#define INIT_TAP_MIN_COUNT       2
+#define MPL_PROD_KEY(ver, rev) (ver * 100 + rev)
+#define NUM_OF_PROD_REVS (ARRAY_SIZE(prod_rev_map))
+/*---- MPU6050 Silicon Revisions ----*/
+#define MPU_SILICON_REV_A2                    1       /* MPU6050A2 Device */
+#define MPU_SILICON_REV_B1                    2       /* MPU6050B1 Device */
+
+#define BIT_PRFTCH_EN                         0x40
+#define BIT_CFG_USER_BANK                     0x20
+#define BITS_MEM_SEL                          0x1f
+
+#define TIME_STAMP_TOR                        5
+#define MAX_CATCH_UP                          5
+#define DEFAULT_ACCL_TRIM                     16384
+#define DEFAULT_GYRO_TRIM                     131
+#define MAX_FIFO_RATE                         1000
+#define MIN_FIFO_RATE                         4
+#define ONE_K_HZ                              1000
+
+/* flick related defines */
+#define DATA_INT                              2097
+#define DATA_MSG_ON                           262144
+#define FLICK_INT_STATUS                      8
+
+/*tap related defines */
+#define INV_TAP                               0x08
+#define INV_NUM_TAP_AXES                      3
+
+#define INV_TAP_AXIS_X_POS                    0x20
+#define INV_TAP_AXIS_X_NEG                    0x10
+#define INV_TAP_AXIS_Y_POS                    0x08
+#define INV_TAP_AXIS_Y_NEG                    0x04
+#define INV_TAP_AXIS_Z_POS                    0x02
+#define INV_TAP_AXIS_Z_NEG                    0x01
+#define INV_TAP_ALL_DIRECTIONS                0x3f
+
+#define INV_TAP_AXIS_X                        0x1
+#define INV_TAP_AXIS_Y                        0x2
+#define INV_TAP_AXIS_Z                        0x4
+
+#define INV_TAP_AXIS_ALL                      \
+		(INV_TAP_AXIS_X            |   \
+		INV_TAP_AXIS_Y             |   \
+		INV_TAP_AXIS_Z)
+
+#define INT_SRC_TAP    0x01
+#define INT_SRC_ORIENT 0x02
+#define INT_SRC_DISPLAY_ORIENT  0x08
+#define INT_SRC_SHAKE           0x10
+
+
+/*orientation related */
+#define INV_X_UP                          0x01
+#define INV_X_DOWN                        0x02
+#define INV_Y_UP                          0x04
+#define INV_Y_DOWN                        0x08
+#define INV_Z_UP                          0x10
+#define INV_Z_DOWN                        0x20
+#define INV_ORIENTATION_ALL               0x3F
+
+#define INV_ORIENTATION_FLIP              0x40
+#define INV_X_AXIS_INDEX                  0x00
+#define INV_Y_AXIS_INDEX                  0x01
+#define INV_Z_AXIS_INDEX                  0x02
+
+#define INV_ELEMENT_1                     0x0001
+#define INV_ELEMENT_2                     0x0002
+#define INV_ELEMENT_3                     0x0004
+#define INV_ELEMENT_4                     0x0008
+#define INV_ELEMENT_5                     0x0010
+#define INV_ELEMENT_6                     0x0020
+#define INV_ELEMENT_7                     0x0040
+#define INV_ELEMENT_8                     0x0080
+#define INV_ALL                           0xFFFF
+#define INV_ELEMENT_MASK                  0x00FF
+#define INV_GYRO_ACC_MASK                 0x007E
+/* scan element definition */
+enum inv_mpu_scan {
+	INV_MPU_SCAN_QUAT_R = 0,
+	INV_MPU_SCAN_QUAT_X,
+	INV_MPU_SCAN_QUAT_Y,
+	INV_MPU_SCAN_QUAT_Z,
+	INV_MPU_SCAN_GYRO_X,
+	INV_MPU_SCAN_GYRO_Y,
+	INV_MPU_SCAN_GYRO_Z,
+	INV_MPU_SCAN_ACCL_X,
+	INV_MPU_SCAN_ACCL_Y,
+	INV_MPU_SCAN_ACCL_Z,
+	INV_MPU_SCAN_MAGN_X,
+	INV_MPU_SCAN_MAGN_Y,
+	INV_MPU_SCAN_MAGN_Z,
+	INV_MPU_SCAN_TIMESTAMP,
+};
+
+enum inv_filter_e {
+	INV_FILTER_256HZ_NOLPF2 = 0,
+	INV_FILTER_188HZ,
+	INV_FILTER_98HZ,
+	INV_FILTER_42HZ,
+	INV_FILTER_20HZ,
+	INV_FILTER_10HZ,
+	INV_FILTER_5HZ,
+	INV_FILTER_2100HZ_NOLPF,
+	NUM_FILTER
+};
+
+enum inv_slave_mode {
+	INV_MODE_SUSPEND,
+	INV_MODE_NORMAL,
+};
+
+/*==== MPU6050B1 MEMORY ====*/
+enum MPU_MEMORY_BANKS {
+	MEM_RAM_BANK_0 = 0,
+	MEM_RAM_BANK_1,
+	MEM_RAM_BANK_2,
+	MEM_RAM_BANK_3,
+	MEM_RAM_BANK_4,
+	MEM_RAM_BANK_5,
+	MEM_RAM_BANK_6,
+	MEM_RAM_BANK_7,
+	MEM_RAM_BANK_8,
+	MEM_RAM_BANK_9,
+	MEM_RAM_BANK_10,
+	MEM_RAM_BANK_11,
+	MPU_MEM_NUM_RAM_BANKS,
+	MPU_MEM_OTP_BANK_0 = 16
+};
+
+/* IIO attribute address */
+enum MPU_IIO_ATTR_ADDR {
+	ATTR_DMP_FLICK_LOWER,
+	ATTR_DMP_FLICK_UPPER,
+	ATTR_DMP_FLICK_COUNTER,
+	ATTR_DMP_FLICK_INT_ON,
+	ATTR_DMP_FLICK_AXIS,
+	ATTR_DMP_FLICK_MSG_ON,
+	ATTR_DMP_PEDOMETER_STEPS,
+	ATTR_DMP_PEDOMETER_TIME,
+	ATTR_DMP_TAP_THRESHOLD,
+	ATTR_DMP_TAP_MIN_COUNT,
+	ATTR_DMP_TAP_ON,
+	ATTR_DMP_TAP_TIME,
+	ATTR_DMP_ON,
+	ATTR_DMP_INT_ON,
+	ATTR_DMP_EVENT_INT_ON,
+	ATTR_DMP_OUTPUT_RATE,
+	ATTR_DMP_ORIENTATION_ON,
+	ATTR_DMP_QUATERNION_ON,
+	ATTR_DMP_DISPLAY_ORIENTATION_ON,
+	ATTR_LPA_MODE,
+	ATTR_LPA_FREQ,
+	ATTR_CLK_SRC,
+	ATTR_SELF_TEST,
+	ATTR_KEY,
+	ATTR_GYRO_MATRIX,
+	ATTR_ACCL_MATRIX,
+	ATTR_COMPASS_MATRIX,
+	ATTR_GYRO_ENABLE,
+	ATTR_ACCL_ENABLE,
+	ATTR_COMPASS_ENABLE,
+	ATTR_POWER_STATE,
+	ATTR_FIRMWARE_LOADED,
+#ifdef CONFIG_INV_TESTING
+	ATTR_I2C_COUNTERS,
+	ATTR_REG_WRITE,
+#endif
+};
+
+enum inv_accl_fs_e {
+	INV_FS_02G = 0,
+	INV_FS_04G,
+	INV_FS_08G,
+	INV_FS_16G,
+	NUM_ACCL_FSR
+};
+
+enum inv_fsr_e {
+	INV_FSR_250DPS = 0,
+	INV_FSR_500DPS,
+	INV_FSR_1000DPS,
+	INV_FSR_2000DPS,
+	NUM_FSR
+};
+
+enum inv_clock_sel_e {
+	INV_CLK_INTERNAL = 0,
+	INV_CLK_PLL,
+	NUM_CLK
+};
+
+ssize_t inv_dmp_firmware_write(struct file *fp, struct kobject *kobj,
+	struct bin_attribute *attr, char *buf, loff_t pos, size_t size);
+ssize_t inv_dmp_firmware_read(struct file *filp,
+				struct kobject *kobj,
+				struct bin_attribute *bin_attr,
+				char *buf, loff_t off, size_t count);
+
+int inv_mpu_configure_ring(struct iio_dev *indio_dev);
+int inv_mpu_probe_trigger(struct iio_dev *indio_dev);
+void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev);
+void inv_mpu_remove_trigger(struct iio_dev *indio_dev);
+int inv_init_config_mpu3050(struct iio_dev *indio_dev);
+int inv_get_silicon_rev_mpu6050(struct inv_mpu_iio_s *st);
+int inv_get_silicon_rev_mpu6500(struct inv_mpu_iio_s *st);
+int set_3050_bypass(struct inv_mpu_iio_s *st, bool enable);
+int inv_register_mpu3050_slave(struct inv_mpu_iio_s *st);
+void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg);
+int inv_switch_3050_gyro_engine(struct inv_mpu_iio_s *st, bool en);
+int inv_switch_3050_accl_engine(struct inv_mpu_iio_s *st, bool en);
+int set_power_mpu3050(struct inv_mpu_iio_s *st, bool power_on);
+int set_inv_enable(struct iio_dev *indio_dev, bool enable);
+int inv_set_interrupt_on_gesture_event(struct inv_mpu_iio_s *st, bool on);
+int inv_send_quaternion(struct inv_mpu_iio_s *st, bool on);
+int inv_set_display_orient_interrupt_dmp(struct inv_mpu_iio_s *st, bool on);
+int inv_enable_orientation_dmp(struct inv_mpu_iio_s *st, bool on);
+int inv_set_fifo_rate(struct inv_mpu_iio_s *st, u16 fifo_rate);
+u16 inv_dmp_get_address(u16 key);
+int inv_q30_mult(int a, int b);
+int inv_set_tap_threshold_dmp(struct inv_mpu_iio_s *st,
+				u32 axis, u16 threshold);
+int inv_set_min_taps_dmp(struct inv_mpu_iio_s *st, u16 min_taps);
+int  inv_set_tap_time_dmp(struct inv_mpu_iio_s *st, u16 time);
+int inv_enable_tap_dmp(struct inv_mpu_iio_s *st, bool on);
+int inv_i2c_read_base(struct inv_mpu_iio_s *st, u16 i2c_addr,
+	u8 reg, u16 length, u8 *data);
+int inv_i2c_single_write_base(struct inv_mpu_iio_s *st,
+	u16 i2c_addr, u8 reg, u8 data);
+int inv_do_test(struct inv_mpu_iio_s *st, int self_test_flag,
+		int *gyro_result, int *accl_result);
+int mpu_memory_write(struct i2c_adapter *i2c_adap,
+			    u8 mpu_addr,
+			    u16 mem_addr,
+			    u32 len, u8 const *data);
+int mpu_memory_read(struct i2c_adapter *i2c_adap,
+			   u8 mpu_addr,
+			   u16 mem_addr,
+			   u32 len, u8 *data);
+int inv_hw_self_test(struct inv_mpu_iio_s *st);
+int inv_hw_self_test_6500(struct inv_mpu_iio_s *st);
+void inv_recover_setting(struct inv_mpu_iio_s *st);
+s64 get_time_ns(void);
+
+#define mem_w(a, b, c) mpu_memory_write(st->sl_handle,\
+			st->i2c_addr, a, b, c)
+#define mem_w_key(key, b, c) mpu_memory_write(st->sl_handle,\
+			st->i2c_addr, inv_dmp_get_address(key), b, c)
+#define inv_i2c_read(st, reg, len, data) \
+	inv_i2c_read_base(st, st->i2c_addr, reg, len, data)
+#define inv_i2c_single_write(st, reg, data) \
+	inv_i2c_single_write_base(st, st->i2c_addr, reg, data)
+#define inv_secondary_read(reg, len, data) \
+	inv_i2c_read_base(st, st->plat_data.secondary_i2c_addr, reg, len, data)
+#define inv_secondary_write(reg, data) \
+	inv_i2c_single_write_base(st, st->plat_data.secondary_i2c_addr, \
+		reg, data)
+#endif  /* #ifndef _INV_MPU_IIO_H_ */
+
diff --git a/drivers/staging/iio/imu/mpu/inv_mpu_misc.c b/drivers/staging/iio/imu/mpu/inv_mpu_misc.c
new file mode 100644
index 0000000..dc0abf2
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu/inv_mpu_misc.c
@@ -0,0 +1,1849 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    inv_mpu_misc.c
+ *      @brief   A sysfs device driver for Invensense mpu.
+ *      @details This file is part of invensense mpu driver code
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/crc32.h>
+
+#include "inv_mpu_iio.h"
+/* DMP defines */
+#define DMP_ORIENTATION_TIME		500
+#define DMP_ORIENTATION_ANGLE		60
+#define DMP_DEFAULT_FIFO_RATE           200
+#define DMP_TAP_SCALE                   (767603923 / 5)
+#define DMP_MULTI_SHIFT                 30
+#define DMP_MULTI_TAP_TIME              500
+#define DMP_SHAKE_REJECT_THRESH         100
+#define DMP_SHAKE_REJECT_TIME           10
+#define DMP_SHAKE_REJECT_TIMEOUT        10
+#define DMP_ANGLE_SCALE                 15
+#define DMP_PRECISION                   1000
+#define DMP_MAX_DIVIDER                 4
+#define DMP_MAX_MIN_TAPS                4
+uint32_t DMP_IMAGE_CRC_VALUES[] = { 0x6590dad6, 0xd37d4599 };
+#define DMP_IMAGE_SIZE                  3065
+
+/*--- Test parameters defaults --- */
+#define DEF_OLDEST_SUPP_PROD_REV    8
+#define DEF_OLDEST_SUPP_SW_REV      2
+
+/* sample rate */
+#define DEF_SELFTEST_SAMPLE_RATE             0
+/* LPF parameter */
+#define DEF_SELFTEST_LPF_PARA                1
+/* full scale setting dps */
+#define DEF_SELFTEST_GYRO_FULL_SCALE         (0 << 3)
+#define DEF_SELFTEST_ACCL_FULL_SCALE         (2 << 3)
+#define DEF_SELFTEST_GYRO_SENS            (32768 / 250)
+/* wait time before collecting data */
+#define DEF_GYRO_WAIT_TIME          50
+#define DEF_ST_STABLE_TIME          200
+#define DEF_GYRO_PACKET_THRESH      DEF_GYRO_WAIT_TIME
+#define DEF_GYRO_THRESH             10
+#define DEF_GYRO_SCALE              131
+#define DEF_ST_PRECISION            1000
+#define DEF_ST_ACCL_FULL_SCALE      8000UL
+#define DEF_ST_SCALE                (1L << 15)
+#define DEF_ST_TRY_TIMES            2
+#define DEF_ST_COMPASS_RESULT_SHIFT 2
+#define DEF_ST_ACCEL_RESULT_SHIFT   1
+
+#define DEF_ST_COMPASS_WAIT_MIN     (10 * 1000)
+#define DEF_ST_COMPASS_WAIT_MAX     (15 * 1000)
+#define DEF_ST_COMPASS_TRY_TIMES    10
+#define DEF_ST_COMPASS_8963_SHIFT   2
+
+#define X                           0
+#define Y                           1
+#define Z                           2
+/*---- MPU6050 notable product revisions ----*/
+#define MPU_PRODUCT_KEY_B1_E1_5      105
+#define MPU_PRODUCT_KEY_B2_F1        431
+/* accelerometer Hw self test min and max bias shift (mg) */
+#define DEF_ACCEL_ST_SHIFT_MIN       300
+#define DEF_ACCEL_ST_SHIFT_MAX       950
+
+#define DEF_ACCEL_ST_SHIFT_DELTA     140
+#define DEF_GYRO_CT_SHIFT_DELTA      140
+/* gyroscope Coriolis self test min and max bias shift (dps) */
+#define DEF_GYRO_CT_SHIFT_MIN        10
+#define DEF_GYRO_CT_SHIFT_MAX        105
+
+static struct test_setup_t test_setup = {
+	.gyro_sens     = DEF_SELFTEST_GYRO_SENS,
+	.sample_rate   = DEF_SELFTEST_SAMPLE_RATE,
+	.lpf           = DEF_SELFTEST_LPF_PARA,
+	.fsr           = DEF_SELFTEST_GYRO_FULL_SCALE,
+	.accl_fs      = DEF_SELFTEST_ACCL_FULL_SCALE
+};
+
+/* NOTE: product entries are in chronological order */
+static const struct prod_rev_map_t prod_rev_map[] = {
+	/* prod_ver = 0 */
+	{MPL_PROD_KEY(0,   1), MPU_SILICON_REV_A2, 131, 16384},
+	{MPL_PROD_KEY(0,   2), MPU_SILICON_REV_A2, 131, 16384},
+	{MPL_PROD_KEY(0,   3), MPU_SILICON_REV_A2, 131, 16384},
+	{MPL_PROD_KEY(0,   4), MPU_SILICON_REV_A2, 131, 16384},
+	{MPL_PROD_KEY(0,   5), MPU_SILICON_REV_A2, 131, 16384},
+	{MPL_PROD_KEY(0,   6), MPU_SILICON_REV_A2, 131, 16384},	/* (A2/C2-1) */
+	/* prod_ver = 1, forced to 0 for MPU6050 A2 */
+	{MPL_PROD_KEY(0,   7), MPU_SILICON_REV_A2, 131, 16384},
+	{MPL_PROD_KEY(0,   8), MPU_SILICON_REV_A2, 131, 16384},
+	{MPL_PROD_KEY(0,   9), MPU_SILICON_REV_A2, 131, 16384},
+	{MPL_PROD_KEY(0,  10), MPU_SILICON_REV_A2, 131, 16384},
+	{MPL_PROD_KEY(0,  11), MPU_SILICON_REV_A2, 131, 16384},	/* (A2/D2-1) */
+	{MPL_PROD_KEY(0,  12), MPU_SILICON_REV_A2, 131, 16384},
+	{MPL_PROD_KEY(0,  13), MPU_SILICON_REV_A2, 131, 16384},
+	{MPL_PROD_KEY(0,  14), MPU_SILICON_REV_A2, 131, 16384},
+	{MPL_PROD_KEY(0,  15), MPU_SILICON_REV_A2, 131, 16384},
+	{MPL_PROD_KEY(0,  27), MPU_SILICON_REV_A2, 131, 16384},	/* (A2/D4)   */
+	/* prod_ver = 1 */
+	{MPL_PROD_KEY(1,  16), MPU_SILICON_REV_B1, 131, 16384},	/* (B1/D2-1) */
+	{MPL_PROD_KEY(1,  17), MPU_SILICON_REV_B1, 131, 16384},	/* (B1/D2-2) */
+	{MPL_PROD_KEY(1,  18), MPU_SILICON_REV_B1, 131, 16384},	/* (B1/D2-3) */
+	{MPL_PROD_KEY(1,  19), MPU_SILICON_REV_B1, 131, 16384},	/* (B1/D2-4) */
+	{MPL_PROD_KEY(1,  20), MPU_SILICON_REV_B1, 131, 16384},	/* (B1/D2-5) */
+	{MPL_PROD_KEY(1,  28), MPU_SILICON_REV_B1, 131, 16384},	/* (B1/D4)   */
+	{MPL_PROD_KEY(1,   1), MPU_SILICON_REV_B1, 131, 16384},	/* (B1/E1-1) */
+	{MPL_PROD_KEY(1,   2), MPU_SILICON_REV_B1, 131, 16384},	/* (B1/E1-2) */
+	{MPL_PROD_KEY(1,   3), MPU_SILICON_REV_B1, 131, 16384},	/* (B1/E1-3) */
+	{MPL_PROD_KEY(1,   4), MPU_SILICON_REV_B1, 131, 16384},	/* (B1/E1-4) */
+	{MPL_PROD_KEY(1,   5), MPU_SILICON_REV_B1, 131, 16384},	/* (B1/E1-5) */
+	{MPL_PROD_KEY(1,   6), MPU_SILICON_REV_B1, 131, 16384},	/* (B1/E1-6) */
+	/* prod_ver = 2 */
+	{MPL_PROD_KEY(2,   7), MPU_SILICON_REV_B1, 131, 16384},	/* (B2/E1-1) */
+	{MPL_PROD_KEY(2,   8), MPU_SILICON_REV_B1, 131, 16384},	/* (B2/E1-2) */
+	{MPL_PROD_KEY(2,   9), MPU_SILICON_REV_B1, 131, 16384},	/* (B2/E1-3) */
+	{MPL_PROD_KEY(2,  10), MPU_SILICON_REV_B1, 131, 16384},	/* (B2/E1-4) */
+	{MPL_PROD_KEY(2,  11), MPU_SILICON_REV_B1, 131, 16384},	/* (B2/E1-5) */
+	{MPL_PROD_KEY(2,  12), MPU_SILICON_REV_B1, 131, 16384},	/* (B2/E1-6) */
+	{MPL_PROD_KEY(2,  29), MPU_SILICON_REV_B1, 131, 16384},	/* (B2/D4)   */
+	/* prod_ver = 3 */
+	{MPL_PROD_KEY(3,  30), MPU_SILICON_REV_B1, 131, 16384},	/* (B2/E2)   */
+	/* prod_ver = 4 */
+	{MPL_PROD_KEY(4,  31), MPU_SILICON_REV_B1, 131,  8192},	/* (B2/F1)   */
+	{MPL_PROD_KEY(4,   1), MPU_SILICON_REV_B1, 131,  8192},	/* (B3/F1)   */
+	{MPL_PROD_KEY(4,   3), MPU_SILICON_REV_B1, 131,  8192},	/* (B4/F1)   */
+	/* prod_ver = 5 */
+	{MPL_PROD_KEY(5,   3), MPU_SILICON_REV_B1, 131, 16384},	/* (B4/F1)   */
+	/* prod_ver = 6 */
+	{MPL_PROD_KEY(6,  19), MPU_SILICON_REV_B1, 131, 16384},	/* (B5/E2)   */
+	/* prod_ver = 7 */
+	{MPL_PROD_KEY(7,  19), MPU_SILICON_REV_B1, 131, 16384},	/* (B5/E2)   */
+	/* prod_ver = 8 */
+	{MPL_PROD_KEY(8,  19), MPU_SILICON_REV_B1, 131, 16384},	/* (B5/E2)   */
+	/* prod_ver = 9 */
+	{MPL_PROD_KEY(9,  19), MPU_SILICON_REV_B1, 131, 16384},	/* (B5/E2)   */
+	/* prod_ver = 10 */
+	{MPL_PROD_KEY(10, 19), MPU_SILICON_REV_B1, 131, 16384}	/* (B5/E2)   */
+};
+
+/*
+*   List of product software revisions
+*
+*   NOTE :
+*   software revision 0 falls back to the old detection method
+*   based off the product version and product revision per the
+*   table above
+*/
+static const struct prod_rev_map_t sw_rev_map[] = {
+	{0,		     0,   0,     0},
+	{1, MPU_SILICON_REV_B1, 131,  8192},	/* rev C */
+	{2, MPU_SILICON_REV_B1, 131, 16384}	/* rev D */
+};
+
+static const int accl_st_tb[31] = {
+	340, 351, 363, 375, 388, 401, 414, 428,
+	443, 458, 473, 489, 506, 523, 541, 559,
+	578, 597, 617, 638, 660, 682, 705, 729,
+	753, 779, 805, 832, 860, 889, 919};
+static const int gyro_6050_st_tb[31] = {
+	3275, 3425, 3583, 3748, 3920, 4100, 4289, 4486,
+	4693, 4909, 5134, 5371, 5618, 5876, 6146, 6429,
+	6725, 7034, 7358, 7696, 8050, 8421, 8808, 9213,
+	9637, 10080, 10544, 11029, 11537, 12067, 12622};
+static const int gyro_3500_st_tb[255] = {
+	2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808,
+	2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041,
+	3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293,
+	3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566,
+	3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862,
+	3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182,
+	4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528,
+	4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903,
+	4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310,
+	5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750,
+	5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226,
+	6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742,
+	6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301,
+	7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906,
+	7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561,
+	8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270,
+	9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038,
+	10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870,
+	10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771,
+	11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746,
+	12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802,
+	13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946,
+	15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184,
+	16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526,
+	17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978,
+	19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550,
+	20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253,
+	22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097,
+	24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093,
+	26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255,
+	28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597,
+	30903, 31212, 31524, 31839, 32157, 32479, 32804};
+
+int mpu_memory_write(struct i2c_adapter *i2c_adap,
+			    u8 mpu_addr,
+			    u16 mem_addr,
+			    u32 len, u8 const *data)
+{
+	u8 bank[2];
+	u8 addr[2];
+	u8 buf[513];
+
+	struct i2c_msg msgs[3];
+	int res;
+
+	if (!data || !i2c_adap)
+		return -EINVAL;
+
+	if (len >= (sizeof(buf) - 1))
+		return -ENOMEM;
+
+	bank[0] = REG_BANK_SEL;
+	bank[1] = mem_addr >> 8;
+
+	addr[0] = REG_MEM_START_ADDR;
+	addr[1] = mem_addr & 0xFF;
+
+	buf[0] = REG_MEM_RW;
+	memcpy(buf + 1, data, len);
+
+	/* write message */
+	msgs[0].addr = mpu_addr;
+	msgs[0].flags = 0;
+	msgs[0].buf = bank;
+	msgs[0].len = sizeof(bank);
+
+	msgs[1].addr = mpu_addr;
+	msgs[1].flags = 0;
+	msgs[1].buf = addr;
+	msgs[1].len = sizeof(addr);
+
+	msgs[2].addr = mpu_addr;
+	msgs[2].flags = 0;
+	msgs[2].buf = (u8 *)buf;
+	msgs[2].len = len + 1;
+
+	res = i2c_transfer(i2c_adap, msgs, 3);
+	if (res != 3) {
+		if (res >= 0)
+			res = -EIO;
+		return res;
+	} else {
+		return 0;
+	}
+}
+
+int mpu_memory_read(struct i2c_adapter *i2c_adap,
+			   u8 mpu_addr,
+			   u16 mem_addr,
+			   u32 len, u8 *data)
+{
+	u8 bank[2];
+	u8 addr[2];
+	u8 buf;
+
+	struct i2c_msg msgs[4];
+	int res;
+
+	if (!data || !i2c_adap)
+		return -EINVAL;
+
+	bank[0] = REG_BANK_SEL;
+	bank[1] = mem_addr >> 8;
+
+	addr[0] = REG_MEM_START_ADDR;
+	addr[1] = mem_addr & 0xFF;
+
+	buf = REG_MEM_RW;
+
+	/* write message */
+	msgs[0].addr = mpu_addr;
+	msgs[0].flags = 0;
+	msgs[0].buf = bank;
+	msgs[0].len = sizeof(bank);
+
+	msgs[1].addr = mpu_addr;
+	msgs[1].flags = 0;
+	msgs[1].buf = addr;
+	msgs[1].len = sizeof(addr);
+
+	msgs[2].addr = mpu_addr;
+	msgs[2].flags = 0;
+	msgs[2].buf = &buf;
+	msgs[2].len = 1;
+
+	msgs[3].addr = mpu_addr;
+	msgs[3].flags = I2C_M_RD;
+	msgs[3].buf = data;
+	msgs[3].len = len;
+
+	res = i2c_transfer(i2c_adap, msgs, 4);
+	if (res != 4) {
+		if (res >= 0)
+			res = -EIO;
+		return res;
+	} else {
+		return 0;
+	}
+}
+
+/**
+ *  index_of_key()- Inverse lookup of the index of an MPL product key .
+ *  @key: the MPL product indentifier also referred to as 'key'.
+ */
+static short index_of_key(u16 key)
+{
+	int i;
+	for (i = 0; i < NUM_OF_PROD_REVS; i++)
+		if (prod_rev_map[i].mpl_product_key == key)
+			return (short)i;
+	return -EINVAL;
+}
+
+int inv_get_silicon_rev_mpu6500(struct inv_mpu_iio_s *st)
+{
+	struct inv_chip_info_s *chip_info = &st->chip_info;
+	int result;
+	u8 whoami;
+
+	result = inv_i2c_read(st, REG_WHOAMI, 1, &whoami);
+	if (result)
+		return result;
+	if (whoami != MPU6500_ID && whoami != MPU9250_ID)
+		return -EINVAL;
+	/* these values are place holders and not real values */
+	chip_info->product_id = MPU6500_PRODUCT_REVISION;
+	chip_info->product_revision = MPU6500_PRODUCT_REVISION;
+	chip_info->silicon_revision = MPU6500_PRODUCT_REVISION;
+	chip_info->software_revision = MPU6500_PRODUCT_REVISION;
+	chip_info->gyro_sens_trim = DEFAULT_GYRO_TRIM;
+	chip_info->accl_sens_trim = DEFAULT_ACCL_TRIM;
+	chip_info->multi = 1;
+
+	return result;
+}
+
+int inv_get_silicon_rev_mpu6050(struct inv_mpu_iio_s *st)
+{
+	int result;
+	struct inv_reg_map_s *reg;
+	u8 prod_ver = 0x00, prod_rev = 0x00;
+	struct prod_rev_map_t *p_rev;
+	u8 bank =
+	    (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0);
+	u16 mem_addr = ((bank << 8) | MEM_ADDR_PROD_REV);
+	u16 key;
+	u8 regs[5];
+	u16 sw_rev;
+	short index;
+	struct inv_chip_info_s *chip_info = &st->chip_info;
+	reg = &st->reg;
+
+	result = inv_i2c_read(st, REG_PRODUCT_ID, 1, &prod_ver);
+	if (result)
+		return result;
+	prod_ver &= 0xf;
+	/*memory read need more time after power up */
+	msleep(POWER_UP_TIME);
+	result = mpu_memory_read(st->sl_handle, st->i2c_addr, mem_addr,
+			1, &prod_rev);
+	if (result)
+		return result;
+	prod_rev >>= 2;
+	/* clean the prefetch and cfg user bank bits */
+	result = inv_i2c_single_write(st, reg->bank_sel, 0);
+	if (result)
+		return result;
+	/* get the software-product version, read from XA_OFFS_L */
+	result = inv_i2c_read(st, REG_XA_OFFS_L_TC,
+				SOFT_PROD_VER_BYTES, regs);
+	if (result)
+		return result;
+
+	sw_rev = (regs[4] & 0x01) << 2 |	/* 0x0b, bit 0 */
+		 (regs[2] & 0x01) << 1 |	/* 0x09, bit 0 */
+		 (regs[0] & 0x01);		/* 0x07, bit 0 */
+	/* if 0, use the product key to determine the type of part */
+	if (sw_rev == 0) {
+		key = MPL_PROD_KEY(prod_ver, prod_rev);
+		if (key == 0)
+			return -EINVAL;
+		index = index_of_key(key);
+		if (index < 0 || index >= NUM_OF_PROD_REVS)
+			return -EINVAL;
+		/* check MPL is compiled for this device */
+		if (prod_rev_map[index].silicon_rev != MPU_SILICON_REV_B1)
+			return -EINVAL;
+		p_rev = (struct prod_rev_map_t *)&prod_rev_map[index];
+	/* if valid, use the software product key */
+	} else if (sw_rev < ARRAY_SIZE(sw_rev_map)) {
+		p_rev = (struct prod_rev_map_t *)&sw_rev_map[sw_rev];
+	} else {
+		return -EINVAL;
+	}
+	chip_info->product_id = prod_ver;
+	chip_info->product_revision = prod_rev;
+	chip_info->silicon_revision = p_rev->silicon_rev;
+	chip_info->software_revision = sw_rev;
+	chip_info->gyro_sens_trim = p_rev->gyro_trim;
+	chip_info->accl_sens_trim = p_rev->accel_trim;
+	if (chip_info->accl_sens_trim == 0)
+		chip_info->accl_sens_trim = DEFAULT_ACCL_TRIM;
+	chip_info->multi = DEFAULT_ACCL_TRIM / chip_info->accl_sens_trim;
+	if (chip_info->multi != 1)
+		pr_info("multi is %d\n", chip_info->multi);
+	return result;
+}
+
+/**
+ *  read_accel_hw_self_test_prod_shift()- read the accelerometer hardware
+ *                                         self-test bias shift calculated
+ *                                         during final production test and
+ *                                         stored in chip non-volatile memory.
+ *  @st:  main data structure.
+ *  @st_prod:   A pointer to an array of 3 elements to hold the values
+ *              for production hardware self-test bias shifts returned to the
+ *              user.
+ */
+static int read_accel_hw_self_test_prod_shift(struct inv_mpu_iio_s *st,
+					int *st_prod)
+{
+	u8 regs[4];
+	u8 shift_code[3];
+	int result, i;
+	st_prod[0] = 0;
+	st_prod[1] = 0;
+	st_prod[2] = 0;
+	result = inv_i2c_read(st, REG_ST_GCT_X, ARRAY_SIZE(regs), regs);
+	if (result)
+		return result;
+	if ((0 == regs[0])  && (0 == regs[1]) &&
+	    (0 == regs[2]) && (0 == regs[3]))
+		return -EINVAL;
+	shift_code[X] = ((regs[0] & 0xE0) >> 3) | ((regs[3] & 0x30) >> 4);
+	shift_code[Y] = ((regs[1] & 0xE0) >> 3) | ((regs[3] & 0x0C) >> 2);
+	shift_code[Z] = ((regs[2] & 0xE0) >> 3) |  (regs[3] & 0x03);
+	for (i = 0; i < 3; i++) {
+		if (shift_code[i] != 0)
+			st_prod[i] = test_setup.accl_sens[i]*
+				accl_st_tb[shift_code[i] - 1];
+	}
+
+	return 0;
+}
+/**
+* inv_check_accl_self_test()- check accel self test. this function returns
+*                              zero as success. A non-zero return value
+*                              indicates failure in self test.
+*  @*st: main data structure.
+*  @*reg_avg: average value of normal test.
+*  @*st_avg:  average value of self test
+*/
+static int inv_check_accl_self_test(struct inv_mpu_iio_s *st,
+	int *reg_avg, int *st_avg){
+	int gravity, reg_z_avg, g_z_sign, fs, j, ret_val;
+	int tmp1;
+	int st_shift_prod[THREE_AXIS], st_shift_cust[THREE_AXIS];
+	int st_shift_ratio[THREE_AXIS];
+	if (st->chip_info.software_revision < DEF_OLDEST_SUPP_SW_REV &&
+	    st->chip_info.product_revision < DEF_OLDEST_SUPP_PROD_REV)
+		return 0;
+	fs = DEF_ST_ACCL_FULL_SCALE;    /* assume +/- 2 mg as typical */
+	g_z_sign = 1;
+	ret_val = 0;
+	test_setup.accl_sens[X] = (u32)(DEF_ST_SCALE *
+						DEF_ST_PRECISION / fs);
+	test_setup.accl_sens[Y] = (u32)(DEF_ST_SCALE *
+						DEF_ST_PRECISION / fs);
+	test_setup.accl_sens[Z] = (u32)(DEF_ST_SCALE *
+						DEF_ST_PRECISION / fs);
+
+	if (MPL_PROD_KEY(st->chip_info.product_id,
+			 st->chip_info.product_revision) ==
+	    MPU_PRODUCT_KEY_B1_E1_5) {
+		/* half sensitivity Z accelerometer parts */
+		test_setup.accl_sens[Z] /= 2;
+	} else {
+		/* half sensitivity X, Y, Z accelerometer parts */
+		test_setup.accl_sens[X] /= st->chip_info.multi;
+		test_setup.accl_sens[Y] /= st->chip_info.multi;
+		test_setup.accl_sens[Z] /= st->chip_info.multi;
+	}
+	gravity = test_setup.accl_sens[Z];
+	reg_z_avg = reg_avg[Z] - g_z_sign * gravity*DEF_ST_PRECISION;
+	read_accel_hw_self_test_prod_shift(st, st_shift_prod);
+	for (j = 0; j < 3; j++) {
+		st_shift_cust[j] = abs(reg_avg[j] - st_avg[j]);
+		if (st_shift_prod[j]) {
+			tmp1 = st_shift_prod[j]/DEF_ST_PRECISION;
+			st_shift_ratio[j] = st_shift_cust[j]/tmp1
+				- DEF_ST_PRECISION;
+			if (st_shift_ratio[j] > DEF_ACCEL_ST_SHIFT_DELTA)
+				ret_val |= 1 << j;
+			if (st_shift_ratio[j] < -DEF_ACCEL_ST_SHIFT_DELTA)
+				ret_val |= 1 << j;
+		} else {
+			if (st_shift_cust[j] <
+				DEF_ACCEL_ST_SHIFT_MIN*gravity)
+				ret_val |= 1 << j;
+			if (st_shift_cust[j] >
+				DEF_ACCEL_ST_SHIFT_MAX*gravity)
+				ret_val |= 1 << j;
+		}
+	}
+
+	return ret_val;
+}
+/**
+* inv_check_3500_gyro_self_test() check gyro self test. this function returns
+*                                 zero as success. A non-zero return value
+*                                 indicates failure in self test.
+*  @*st: main data structure.
+*  @*reg_avg: average value of normal test.
+*  @*st_avg:  average value of self test
+*/
+
+static int inv_check_3500_gyro_self_test(struct inv_mpu_iio_s *st,
+	int *reg_avg, int *st_avg){
+	int result;
+	int gst[3], ret_val;
+	int gst_otp[3], i;
+	u8 st_code[THREE_AXIS];
+	ret_val = 0;
+
+	for (i = 0; i < 3; i++)
+		gst[i] = st_avg[i] - reg_avg[i];
+	result = inv_i2c_read(st, REG_3500_OTP, THREE_AXIS, st_code);
+	if (result)
+		return result;
+	gst_otp[0] = 0;
+	gst_otp[1] = 0;
+	gst_otp[2] = 0;
+	for (i = 0; i < 3; i++) {
+		if (st_code[i] != 0)
+			gst_otp[i] = gyro_3500_st_tb[st_code[i] - 1];
+	}
+	for (i = 0; i < 3; i++) {
+		if (gst_otp[i] == 0) {
+			if (abs(gst[i]) * 4 < 60 * 2 * DEF_ST_PRECISION *
+					DEF_GYRO_SCALE)
+				ret_val |= (1 << i);
+		} else {
+			if (abs(gst[i]/gst_otp[i] - DEF_ST_PRECISION) >
+					DEF_GYRO_CT_SHIFT_DELTA)
+				ret_val |= (1 << i);
+		}
+	}
+	for (i = 0; i < 3; i++) {
+		if (abs(reg_avg[i]) * 4 > 20 * 2 *
+		    DEF_ST_PRECISION*DEF_GYRO_SCALE)
+			ret_val |= (1 << i);
+	}
+
+	return ret_val;
+}
+
+/**
+* inv_check_6050_gyro_self_test() - check 6050 gyro self test. this function
+*                                   returns zero as success. A non-zero return
+*                                   value indicates failure in self test.
+*  @*st: main data structure.
+*  @*reg_avg: average value of normal test.
+*  @*st_avg:  average value of self test
+*/
+static int inv_check_6050_gyro_self_test(struct inv_mpu_iio_s *st,
+	int *reg_avg, int *st_avg){
+	int result;
+	int ret_val;
+	int ct_shift_prod[3], st_shift_cust[3], st_shift_ratio[3], i;
+	u8 regs[3];
+	if (st->chip_info.software_revision < DEF_OLDEST_SUPP_SW_REV &&
+	    st->chip_info.product_revision < DEF_OLDEST_SUPP_PROD_REV)
+		return 0;
+
+	ret_val = 0;
+	result = inv_i2c_read(st, REG_ST_GCT_X, 3, regs);
+	if (result)
+		return result;
+	regs[X] &= 0x1f;
+	regs[Y] &= 0x1f;
+	regs[Z] &= 0x1f;
+
+	for (i = 0; i < 3; i++) {
+		if (regs[i] != 0)
+			ct_shift_prod[i] = gyro_6050_st_tb[regs[i] - 1];
+		else
+			ct_shift_prod[i] = 0;
+	}
+	for (i = 0; i < 3; i++) {
+		st_shift_cust[i] = abs(reg_avg[i] - st_avg[i]);
+		if (ct_shift_prod[i]) {
+			st_shift_ratio[i] = st_shift_cust[i] /
+				ct_shift_prod[i] - DEF_ST_PRECISION;
+			if (st_shift_ratio[i] > DEF_GYRO_CT_SHIFT_DELTA)
+				ret_val |= 1 << i;
+			if (st_shift_ratio[i] < -DEF_GYRO_CT_SHIFT_DELTA)
+				ret_val |= 1 << i;
+		} else {
+			if (st_shift_cust[i] < DEF_ST_PRECISION *
+				DEF_GYRO_CT_SHIFT_MIN * test_setup.gyro_sens)
+				ret_val |= 1 << i;
+			if (st_shift_cust[i] > DEF_ST_PRECISION *
+				DEF_GYRO_CT_SHIFT_MAX * test_setup.gyro_sens)
+				ret_val |= 1 << i;
+		}
+	}
+	for (i = 0; i < 3; i++) {
+		if (abs(reg_avg[i]) * 4 > 20 * 2 *
+		    DEF_ST_PRECISION * DEF_GYRO_SCALE)
+			ret_val |= (1 << i);
+	}
+
+	return ret_val;
+}
+
+/**
+ *  inv_do_test() - do the actual test of self testing
+ */
+int inv_do_test(struct inv_mpu_iio_s *st, int self_test_flag,
+		int *gyro_result, int *accl_result)
+{
+	struct inv_reg_map_s *reg;
+	int result, i, j, packet_size;
+	u8 data[BYTES_PER_SENSOR * 2], has_accl;
+	int fifo_count, packet_count, ind;
+
+	reg = &st->reg;
+	has_accl = (st->chip_type != INV_ITG3500);
+	packet_size = BYTES_PER_SENSOR*(1 + has_accl);
+
+	result = inv_i2c_single_write(st, reg->int_enable, 0);
+	if (result)
+		return result;
+	/* disable the sensor output to FIFO */
+	result = inv_i2c_single_write(st, reg->fifo_en, 0);
+	if (result)
+		return result;
+	/* disable fifo reading */
+	result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+	if (result)
+		return result;
+	/* clear FIFO */
+	result = inv_i2c_single_write(st, reg->user_ctrl, BIT_FIFO_RST);
+	if (result)
+		return result;
+	/* setup parameters */
+	result = inv_i2c_single_write(st, reg->lpf, test_setup.lpf);
+	if (result)
+		return result;
+	result = inv_i2c_single_write(st, reg->sample_rate_div,
+		test_setup.sample_rate);
+	if (result)
+		return result;
+	result = inv_i2c_single_write(st, reg->gyro_config,
+		self_test_flag | test_setup.fsr);
+	if (result)
+		return result;
+	if (has_accl) {
+		result = inv_i2c_single_write(st, reg->accl_config,
+			self_test_flag | test_setup.accl_fs);
+		if (result)
+			return result;
+	}
+	/* wait for the output to get stable */
+	if (self_test_flag)
+		msleep(DEF_ST_STABLE_TIME);
+
+	/* enable FIFO reading */
+	result = inv_i2c_single_write(st, reg->user_ctrl, BIT_FIFO_EN);
+	if (result)
+		return result;
+	/* enable sensor output to FIFO */
+	result = inv_i2c_single_write(st, reg->fifo_en, BITS_GYRO_OUT
+		| (has_accl << 3));
+	if (result)
+		return result;
+	mdelay(DEF_GYRO_WAIT_TIME);
+	/* stop sending data to FIFO */
+	result = inv_i2c_single_write(st, reg->fifo_en, 0);
+	if (result)
+		return result;
+	result = inv_i2c_read(st, reg->fifo_count_h, FIFO_COUNT_BYTE, data);
+	if (result)
+		return result;
+	fifo_count = be16_to_cpup((__be16 *)(&data[0]));
+	packet_count = fifo_count / packet_size;
+	for (i = 0; i < 3; i++) {
+		gyro_result[i] = 0;
+		accl_result[i] = 0;
+	}
+	if (abs(packet_count - DEF_GYRO_PACKET_THRESH) > DEF_GYRO_THRESH)
+		return -EAGAIN;
+
+	for (i = 0; i < packet_count; i++) {
+		/* getting FIFO data */
+		result = inv_i2c_read(st, reg->fifo_r_w,
+			packet_size, data);
+		if (result)
+			return result;
+		ind = 0;
+		if (has_accl) {
+			for (j = 0; j < THREE_AXIS; j++)
+				accl_result[j] +=
+					(short)be16_to_cpup(
+						(__be16 *)(&data[ind + 2 * j]));
+				ind += BYTES_PER_SENSOR;
+		}
+		for (j = 0; j < THREE_AXIS; j++)
+			gyro_result[j] +=
+				(short)be16_to_cpup(
+					(__be16 *)(&data[ind + 2 * j]));
+	}
+
+	gyro_result[0] = gyro_result[0] * DEF_ST_PRECISION / packet_count;
+	gyro_result[1] = gyro_result[1] * DEF_ST_PRECISION / packet_count;
+	gyro_result[2] = gyro_result[2] * DEF_ST_PRECISION / packet_count;
+	if (has_accl) {
+		accl_result[0] =
+			accl_result[0] * DEF_ST_PRECISION / packet_count;
+		accl_result[1] =
+			accl_result[1] * DEF_ST_PRECISION / packet_count;
+		accl_result[2] =
+			accl_result[2] * DEF_ST_PRECISION / packet_count;
+	}
+
+	return 0;
+}
+
+/**
+ *  inv_recover_setting() recover the old settings after everything is done
+ */
+
+void inv_recover_setting(struct inv_mpu_iio_s *st)
+{
+	struct inv_reg_map_s *reg;
+	int data;
+	struct iio_dev *indio = iio_priv_to_dev(st);
+
+	reg = &st->reg;
+	set_inv_enable(indio, st->chip_config.enable);
+	inv_i2c_single_write(st, reg->gyro_config,
+			     st->chip_config.fsr << GYRO_CONFIG_FSR_SHIFT);
+	inv_i2c_single_write(st, reg->lpf, st->chip_config.lpf);
+	data = ONE_K_HZ/st->chip_config.fifo_rate - 1;
+	inv_i2c_single_write(st, reg->sample_rate_div, data);
+	if (INV_ITG3500 != st->chip_type) {
+		inv_i2c_single_write(st, reg->accl_config,
+				     (st->chip_config.accl_fs <<
+				     ACCL_CONFIG_FSR_SHIFT));
+	}
+	st->set_power_state(st, !st->chip_config.is_asleep);
+}
+
+static int inv_check_compass_self_test(struct inv_mpu_iio_s *st)
+{
+	int result;
+	u8 data[6];
+	u8 counter, cntl;
+	short x, y, z;
+	u8 *sens;
+	sens = st->chip_info.compass_sens;
+
+	/* set to bypass mode */
+	result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+				st->plat_data.int_config | BIT_BYPASS_EN);
+	if (result) {
+		result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+				st->plat_data.int_config);
+		return result;
+	}
+	/* set to power down mode */
+	result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PD);
+	if (result)
+		goto AKM_fail;
+
+	/* write 1 to ASTC register */
+	result = inv_secondary_write(REG_AKM_ST_CTRL, DATA_AKM_SELF_TEST);
+	if (result)
+		goto AKM_fail;
+	/* set self test mode */
+	result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_ST);
+	if (result)
+		goto AKM_fail;
+	counter = DEF_ST_COMPASS_TRY_TIMES;
+	while (counter > 0) {
+		usleep_range(DEF_ST_COMPASS_WAIT_MIN, DEF_ST_COMPASS_WAIT_MAX);
+		result = inv_secondary_read(REG_AKM_STATUS, 1, data);
+		if (result)
+			goto AKM_fail;
+		if ((data[0] & DATA_AKM_DRDY) == 0)
+			counter--;
+		else
+			counter = 0;
+	}
+	if ((data[0] & DATA_AKM_DRDY) == 0) {
+		result = -EINVAL;
+		goto AKM_fail;
+	}
+	result = inv_secondary_read(REG_AKM_MEASURE_DATA,
+					BYTES_PER_SENSOR, data);
+	if (result)
+		goto AKM_fail;
+
+	x = le16_to_cpup((__le16 *)(&data[0]));
+	y = le16_to_cpup((__le16 *)(&data[2]));
+	z = le16_to_cpup((__le16 *)(&data[4]));
+	x = ((x * (sens[0] + 128)) >> 8);
+	y = ((y * (sens[1] + 128)) >> 8);
+	z = ((z * (sens[2] + 128)) >> 8);
+	if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) {
+		result = inv_secondary_read(REG_AKM8963_CNTL1, 1, &cntl);
+		if (result)
+			goto AKM_fail;
+		if (0 == (cntl & DATA_AKM8963_BIT)) {
+			x <<= DEF_ST_COMPASS_8963_SHIFT;
+			y <<= DEF_ST_COMPASS_8963_SHIFT;
+			z <<= DEF_ST_COMPASS_8963_SHIFT;
+		}
+	}
+	result = -EINVAL;
+	if (x > st->compass_st_upper[X] || x < st->compass_st_lower[X])
+		goto AKM_fail;
+	if (y > st->compass_st_upper[Y] || y < st->compass_st_lower[Y])
+		goto AKM_fail;
+	if (z > st->compass_st_upper[Z] || z < st->compass_st_lower[Z])
+		goto AKM_fail;
+	result = 0;
+AKM_fail:
+	/*write 0 to ASTC register */
+	result |= inv_secondary_write(REG_AKM_ST_CTRL, 0);
+	/*set to power down mode */
+	result |= inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PD);
+	/*restore to non-bypass mode */
+	result |= inv_i2c_single_write(st, REG_INT_PIN_CFG,
+			st->plat_data.int_config);
+	return result;
+}
+
+static int inv_power_up_self_test(struct inv_mpu_iio_s *st)
+{
+	int result;
+	result = inv_i2c_single_write(st, st->reg.pwr_mgmt_1, INV_CLK_PLL);
+	if (result)
+		return result;
+	msleep(POWER_UP_TIME);
+	result = inv_i2c_single_write(st, st->reg.pwr_mgmt_2, 0);
+	if (result)
+		return result;
+	msleep(SENSOR_UP_TIME);
+
+	return 0;
+}
+
+/**
+ *  inv_hw_self_test() - main function to do hardware self test
+ */
+int inv_hw_self_test(struct inv_mpu_iio_s *st)
+{
+	int result;
+	int gyro_bias_st[THREE_AXIS], gyro_bias_regular[THREE_AXIS];
+	int accl_bias_st[THREE_AXIS], accl_bias_regular[THREE_AXIS];
+	int test_times;
+	char compass_result, accel_result, gyro_result;
+	if (st->chip_config.is_asleep      ||
+	    st->chip_config.lpa_mode       ||
+	    (!st->chip_config.gyro_enable) ||
+	    (!st->chip_config.accl_enable)) {
+		result = inv_power_up_self_test(st);
+		if (result)
+			return result;
+	}
+	compass_result = 0;
+	accel_result   = 0;
+	gyro_result    = 0;
+	test_times = DEF_ST_TRY_TIMES;
+	while (test_times > 0) {
+		result = inv_do_test(st, 0, gyro_bias_regular,
+			accl_bias_regular);
+		if (result == -EAGAIN)
+			test_times--;
+		else
+			test_times = 0;
+	}
+	if (result)
+		goto test_fail;
+
+	test_times = DEF_ST_TRY_TIMES;
+	while (test_times > 0) {
+		result = inv_do_test(st, BITS_SELF_TEST_EN, gyro_bias_st,
+					accl_bias_st);
+		if (result == -EAGAIN)
+			test_times--;
+		else
+			break;
+	}
+	if (result)
+		goto test_fail;
+	if (st->chip_type == INV_ITG3500) {
+		gyro_result = !inv_check_3500_gyro_self_test(st,
+			gyro_bias_regular, gyro_bias_st);
+	} else {
+		if (st->chip_config.has_compass)
+			compass_result = !inv_check_compass_self_test(st);
+		accel_result = !inv_check_accl_self_test(st,
+			accl_bias_regular, accl_bias_st);
+		gyro_result = !inv_check_6050_gyro_self_test(st,
+			gyro_bias_regular, gyro_bias_st);
+	}
+test_fail:
+	inv_recover_setting(st);
+
+	return (compass_result << DEF_ST_COMPASS_RESULT_SHIFT) |
+		(accel_result << DEF_ST_ACCEL_RESULT_SHIFT) | gyro_result;
+}
+
+/**
+ *  inv_hw_self_test_6500() - main function to do hardware self test for 6500
+ */
+int inv_hw_self_test_6500(struct inv_mpu_iio_s *st)
+{
+	int compass_result;
+	compass_result = !inv_check_compass_self_test(st);
+	return compass_result << DEF_ST_COMPASS_RESULT_SHIFT;
+}
+
+static int inv_load_firmware(struct inv_mpu_iio_s *st,
+	u8 *data, int size)
+{
+	int bank, write_size;
+	int result;
+	u16 memaddr;
+
+	/* Write and verify memory */
+	for (bank = 0; size > 0; bank++,
+		size -= write_size,
+		data += write_size) {
+		if (size > MPU_MEM_BANK_SIZE)
+			write_size = MPU_MEM_BANK_SIZE;
+		else
+			write_size = size;
+
+		memaddr = ((bank << 8) | 0x00);
+
+		result = mem_w(memaddr, write_size, data);
+		if (result)
+			return result;
+	}
+	return 0;
+}
+
+static int inv_verify_firmware(struct inv_mpu_iio_s *st,
+	u8 *data, int size)
+{
+	int bank, write_size;
+	int result;
+	u16 memaddr;
+	u8 firmware[MPU_MEM_BANK_SIZE];
+
+	/* Write and verify memory */
+	for (bank = 0; size > 0; bank++,
+		size -= write_size,
+		data += write_size) {
+		if (size > MPU_MEM_BANK_SIZE)
+			write_size = MPU_MEM_BANK_SIZE;
+		else
+			write_size = size;
+
+		memaddr = ((bank << 8) | 0x00);
+		result = mpu_memory_read(st->sl_handle,
+			st->i2c_addr, memaddr, write_size, firmware);
+		if (result)
+			return result;
+		if (0 != memcmp(firmware, data, write_size))
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static int inv_set_fifo_div(struct inv_mpu_iio_s *st,
+		u16 fifoRate)
+{
+	u8 regs[2];
+	int result = 0;
+	/*For some reason DINAC4 is defined as 0xb8, but DINBC4 is not*/
+	const u8 regs_end[12] = {DINAFE, DINAF2, DINAAB, 0xc4,
+					DINAAA, DINAF1, DINADF, DINADF,
+					0xbb, 0xaf, DINADF, DINADF};
+
+	regs[0] = (u8)((fifoRate >> 8) & 0xff);
+	regs[1] = (u8)(fifoRate & 0xff);
+	result = mem_w_key(KEY_D_0_22, ARRAY_SIZE(regs), regs);
+	if (result)
+		return result;
+
+	/*Modify the FIFO handler to reset the tap/orient interrupt flags*/
+	/* each time the FIFO handler runs*/
+	result = mem_w_key(KEY_CFG_6, ARRAY_SIZE(regs_end), regs_end);
+
+	return result;
+}
+
+int inv_send_quaternion(struct inv_mpu_iio_s *st, bool on)
+{
+	const u8 regs_on[] = {DINBC0, DINBC2,
+					 DINBC4, DINBC6};
+	const u8 regs_off[] = {DINA80, DINA80,
+					  DINA80, DINA80};
+	const u8 *regs;
+	u8 result;
+	if (on)
+		regs = regs_on;
+	else
+		regs = regs_off;
+	result = mem_w_key(KEY_CFG_LP_QUAT, ARRAY_SIZE(regs_on), regs);
+
+	return result;
+}
+
+int inv_set_display_orient_interrupt_dmp(struct inv_mpu_iio_s *st,
+						bool on)
+{
+	/*Turn on the display orientation interrupt in the DMP*/
+	int result;
+	u8  regs[] = {0xd8};
+
+	if (on)
+		regs[0] = 0xd9;
+	result = mem_w_key(KEY_CFG_DISPLAY_ORIENT_INT, 1, regs);
+	return result;
+}
+
+int inv_set_fifo_rate(struct inv_mpu_iio_s *st, u16 fifo_rate)
+{
+	u8 divider;
+	int result;
+
+	divider = (u8)(ONE_K_HZ / fifo_rate) - 1;
+	if (divider > DMP_MAX_DIVIDER) {
+		st->sample_divider = DMP_MAX_DIVIDER;
+		st->fifo_divider =
+			(u8)(DMP_DEFAULT_FIFO_RATE / fifo_rate) - 1;
+	} else {
+		st->sample_divider = divider;
+		st->fifo_divider = 0;
+	}
+
+	result = inv_set_fifo_div(st, st->fifo_divider);
+	return result;
+}
+
+static int inv_set_tap_interrupt_dmp(struct inv_mpu_iio_s *st,
+	u8 on)
+{
+	int result;
+	u8  regs[] = {0};
+
+	if (on)
+		regs[0] = 0xf8;
+	else
+		regs[0] = DINAD8;
+	result = mem_w_key(KEY_CFG_20, ARRAY_SIZE(regs), regs);
+	if (result)
+		return result;
+	return result;
+}
+
+static int inv_set_orientation_interrupt_dmp(struct inv_mpu_iio_s *st,
+			u8 on)
+{
+	int result;
+	u8  regs[2];
+	if (on) {
+		regs[0] = DINBF8;
+		regs[1] = DINBF8;
+	} else {
+		regs[0] = DINAD8;
+		regs[1] = DINAD8;
+	}
+	result = mem_w_key(KEY_CFG_ORIENT_IRQ_1, ARRAY_SIZE(regs), regs);
+	if (result)
+		return result;
+	return result;
+}
+
+int inv_set_tap_threshold_dmp(struct inv_mpu_iio_s *st,
+				u32 axis, u16 threshold)
+{
+	/* Sets the tap threshold in the dmp
+	Simultaneously sets secondary tap threshold to help correct the tap
+	direction for soft taps */
+	int result;
+	/* DMP Algorithm */
+	u8 data[2];
+	int sampleDivider;
+	int scaledThreshold;
+	u32 dmpThreshold;
+	u8 sample_div;
+	const u32  accel_sens = (0x20000000 / 0x00010000);
+
+	if ((axis & ~(INV_TAP_AXIS_ALL)) || (threshold > (1 << 15)))
+		return -EINVAL;
+	sample_div = st->sample_divider;
+
+	sampleDivider = (1 + sample_div);
+	/* Scale factor corresponds linearly using
+	* 0  : 0
+	* 25 : 0.0250  g/ms
+	* 50 : 0.0500  g/ms
+	* 100: 1.0000  g/ms
+	* 200: 2.0000  g/ms
+	* 400: 4.0000  g/ms
+	* 800: 8.0000  g/ms
+	*/
+	/*multiply by 1000 to avoid floating point 1000/1000*/
+	scaledThreshold = threshold;
+	/* Convert to per sample */
+	scaledThreshold *= sampleDivider;
+
+	/* Scale to DMP 16 bit value */
+	if (accel_sens != 0)
+		dmpThreshold = (u32)(scaledThreshold * accel_sens);
+	else
+		return -EINVAL;
+	dmpThreshold = dmpThreshold / DMP_PRECISION;
+
+	data[0] = dmpThreshold >> 8;
+	data[1] = dmpThreshold & 0xFF;
+
+	/* MPL algorithm */
+	if (axis & INV_TAP_AXIS_X) {
+		result = mem_w_key(KEY_DMP_TAP_THR_X, ARRAY_SIZE(data), data);
+		if (result)
+			return result;
+
+		/*Also set additional threshold for correcting the direction
+		of taps that were very near the threshold. */
+		data[0] = (dmpThreshold * 3 / 4) >> 8;
+		data[1] = (dmpThreshold * 3 / 4) & 0xFF;
+		result = mem_w_key(KEY_D_1_36, ARRAY_SIZE(data), data);
+		if (result)
+			return result;
+	}
+	if (axis & INV_TAP_AXIS_Y) {
+		result = mem_w_key(KEY_DMP_TAP_THR_Y, 2, data);
+		if (result)
+			return result;
+		data[0] = (dmpThreshold * 3 / 4) >> 8;
+		data[1] = (dmpThreshold * 3 / 4) & 0xFF;
+
+		result = mem_w_key(KEY_D_1_40, ARRAY_SIZE(data), data);
+		if (result)
+			return result;
+	}
+	if (axis & INV_TAP_AXIS_Z) {
+		result = mem_w_key(KEY_DMP_TAP_THR_Z, ARRAY_SIZE(data), data);
+		if (result)
+			return result;
+		data[0] = (dmpThreshold * 3 / 4) >> 8;
+		data[1] = (dmpThreshold * 3 / 4) & 0xFF;
+
+		result = mem_w_key(KEY_D_1_44, ARRAY_SIZE(data), data);
+		if (result)
+			return result;
+	}
+	return 0;
+}
+
+static int inv_set_tap_axes_dmp(struct inv_mpu_iio_s *st,
+				u32 axes)
+{
+	/* Sets a mask in the DMP that indicates what tap events
+	should result in an interrupt */
+	u8 regs[4];
+	u8 result;
+
+	/* check if any spurious bit other the ones expected are set */
+	if (axes & (~(INV_TAP_ALL_DIRECTIONS)))
+		return -EINVAL;
+
+	regs[0] = (u8)axes;
+	result = mem_w_key(KEY_D_1_72, 1, regs);
+
+	return result;
+}
+
+int inv_set_min_taps_dmp(struct inv_mpu_iio_s *st,
+				u16 min_taps) {
+	/*Indicates the minimum number of consecutive taps required
+		before the DMP will generate an interrupt */
+	u8 regs[1];
+	u8 result;
+	/* check if any spurious bit other the ones expected are set */
+	if ((min_taps > DMP_MAX_MIN_TAPS) || (min_taps < 1))
+		return -EINVAL;
+	regs[0] = (u8)(min_taps-1);
+	result = mem_w_key(KEY_D_1_79, ARRAY_SIZE(regs), regs);
+
+	return result;
+}
+
+int  inv_set_tap_time_dmp(struct inv_mpu_iio_s *st, u16 time)
+{
+	/* Determines how long after a tap the DMP requires before
+	  another tap can be registered*/
+	int result;
+	/* DMP Algorithm */
+	u16 dmpTime;
+	u8 data[2];
+	u8 sampleDivider;
+
+	sampleDivider = st->sample_divider;
+	sampleDivider++;
+
+	/* 60 ms minimum time added */
+	dmpTime = ((time) / sampleDivider);
+	data[0] = dmpTime >> 8;
+	data[1] = dmpTime & 0xFF;
+
+	result = mem_w_key(KEY_DMP_TAPW_MIN, ARRAY_SIZE(data), data);
+
+	return result;
+}
+
+static int inv_set_multiple_tap_time_dmp(struct inv_mpu_iio_s *st,
+					u32 time)
+{
+	/*Determines how close together consecutive taps must occur
+	to be considered double/triple taps*/
+	int result;
+	/* DMP Algorithm */
+	u16 dmpTime;
+	u8 data[2];
+	u8 sampleDivider;
+
+	sampleDivider = st->sample_divider;
+	sampleDivider++;
+
+	/* 60 ms minimum time added */
+	dmpTime = ((time) / sampleDivider);
+	data[0] = dmpTime >> 8;
+	data[1] = dmpTime & 0xFF;
+	result = mem_w_key(KEY_D_1_218, ARRAY_SIZE(data), data);
+
+	return result;
+}
+
+int inv_q30_mult(int a, int b)
+{
+	u64 temp;
+	int result;
+	temp = (u64)a * b;
+	result = (int)(temp >> DMP_MULTI_SHIFT);
+
+	return result;
+}
+
+static u16 inv_row_2_scale(const signed char *row)
+{
+	u16 b;
+
+	if (row[0] > 0)
+		b = 0;
+	else if (row[0] < 0)
+		b = 4;
+	else if (row[1] > 0)
+		b = 1;
+	else if (row[1] < 0)
+		b = 5;
+	else if (row[2] > 0)
+		b = 2;
+	else if (row[2] < 0)
+		b = 6;
+	else
+		b = 7;
+
+	return b;
+}
+
+/** Converts an orientation matrix made up of 0,+1,and -1 to a scalar
+*	representation.
+* @param[in] mtx Orientation matrix to convert to a scalar.
+* @return Description of orientation matrix. The lowest 2 bits (0 and 1)
+* represent the column the one is on for the
+* first row, with the bit number 2 being the sign. The next 2 bits
+* (3 and 4) represent
+* the column the one is on for the second row with bit number 5 being
+* the sign.
+* The next 2 bits (6 and 7) represent the column the one is on for the
+* third row with
+* bit number 8 being the sign. In binary the identity matrix would therefor
+* be: 010_001_000 or 0x88 in hex.
+*/
+static u16 inv_orientation_matrix_to_scaler(const signed char *mtx)
+{
+
+	u16 scalar;
+	scalar = inv_row_2_scale(mtx);
+	scalar |= inv_row_2_scale(mtx + 3) << 3;
+	scalar |= inv_row_2_scale(mtx + 6) << 6;
+
+	return scalar;
+}
+
+static int inv_gyro_dmp_cal(struct inv_mpu_iio_s *st)
+{
+	int inv_gyro_orient;
+	u8 regs[3];
+	int result;
+
+	u8 tmpD = DINA4C;
+	u8 tmpE = DINACD;
+	u8 tmpF = DINA6C;
+
+	inv_gyro_orient =
+		inv_orientation_matrix_to_scaler(st->plat_data.orientation);
+
+	if ((inv_gyro_orient & 3) == 0)
+		regs[0] = tmpD;
+	else if ((inv_gyro_orient & 3) == 1)
+		regs[0] = tmpE;
+	else if ((inv_gyro_orient & 3) == 2)
+		regs[0] = tmpF;
+	if ((inv_gyro_orient & 0x18) == 0)
+		regs[1] = tmpD;
+	else if ((inv_gyro_orient & 0x18) == 0x8)
+		regs[1] = tmpE;
+	else if ((inv_gyro_orient & 0x18) == 0x10)
+		regs[1] = tmpF;
+	if ((inv_gyro_orient & 0xc0) == 0)
+		regs[2] = tmpD;
+	else if ((inv_gyro_orient & 0xc0) == 0x40)
+		regs[2] = tmpE;
+	else if ((inv_gyro_orient & 0xc0) == 0x80)
+		regs[2] = tmpF;
+
+	result = mem_w_key(KEY_FCFG_1, 3, regs);
+	if (result)
+		return result;
+
+	if (inv_gyro_orient & 4)
+		regs[0] = DINA36 | 1;
+	else
+		regs[0] = DINA36;
+	if (inv_gyro_orient & 0x20)
+		regs[1] = DINA56 | 1;
+	else
+		regs[1] = DINA56;
+	if (inv_gyro_orient & 0x100)
+		regs[2] = DINA76 | 1;
+	else
+		regs[2] = DINA76;
+
+	result = mem_w_key(KEY_FCFG_3, ARRAY_SIZE(regs), regs);
+
+	return result;
+}
+
+static int inv_accel_dmp_cal(struct inv_mpu_iio_s *st)
+{
+	int inv_accel_orient;
+	int result;
+	u8 regs[3];
+	const u8 tmp[3] = { DINA0C, DINAC9, DINA2C };
+	inv_accel_orient =
+		inv_orientation_matrix_to_scaler(st->plat_data.orientation);
+
+	regs[0] = tmp[inv_accel_orient & 3];
+	regs[1] = tmp[(inv_accel_orient >> 3) & 3];
+	regs[2] = tmp[(inv_accel_orient >> 6) & 3];
+	result = mem_w_key(KEY_FCFG_2, 3, regs);
+	if (result)
+		return result;
+
+	regs[0] = DINA26;
+	regs[1] = DINA46;
+	regs[2] = DINA66;
+	if (inv_accel_orient & 4)
+		regs[0] |= 1;
+	if (inv_accel_orient & 0x20)
+		regs[1] |= 1;
+	if (inv_accel_orient & 0x100)
+		regs[2] |= 1;
+	result = mem_w_key(KEY_FCFG_7, ARRAY_SIZE(regs), regs);
+
+	return result;
+}
+
+static int inv_set_gyro_sf_dmp(struct inv_mpu_iio_s *st)
+{
+	/*The gyro threshold, in dps, above which taps will be rejected*/
+	int result, out;
+	/* DMP Algorithm */
+	u8 sampleDivider;
+	u8 *regs;
+	u32 gyro_sf;
+	const u32 gyro_sens = 0x03e80000;
+
+	sampleDivider = st->sample_divider;
+	gyro_sf = inv_q30_mult(gyro_sens,
+			(int)(DMP_TAP_SCALE * (sampleDivider + 1)));
+
+	out = cpu_to_be32p(&gyro_sf);
+	regs = (u8 *)&out;
+	result = mem_w_key(KEY_D_0_104, sizeof(out), regs);
+
+	return result;
+}
+
+static int inv_set_shake_reject_thresh_dmp(struct inv_mpu_iio_s *st,
+						int thresh)
+{	/*THIS FUNCTION FAILS MEM_W*/
+	/*The gyro threshold, in dps, above which taps will be rejected */
+	int result, out;
+	/* DMP Algorithm */
+	u8 sampleDivider;
+	int thresh_scaled;
+	u8 *regs;
+	u32 gyro_sf;
+	const u32 gyro_sens = 0x03e80000;
+	sampleDivider = st->sample_divider;
+	gyro_sf = inv_q30_mult(gyro_sens, (int)(DMP_TAP_SCALE *
+			(sampleDivider + 1)));
+	/* We're in units of DPS, convert it back to chip units*/
+	/*split the operation to aviod overflow of integer*/
+	thresh_scaled = gyro_sens / (1L << 16);
+	thresh_scaled = thresh_scaled / thresh;
+	thresh_scaled = gyro_sf / thresh_scaled;
+	out = cpu_to_be32p(&thresh_scaled);
+	regs = (u8 *)&out;
+
+	result = mem_w_key(KEY_D_1_92, sizeof(out), regs);
+	return result;
+}
+
+static int inv_set_shake_reject_time_dmp(struct inv_mpu_iio_s *st,
+						u32 time)
+{
+	/* How long a gyro axis must remain above its threshold
+	before taps are rejected */
+	int result;
+	/* DMP Algorithm */
+	u16 dmpTime;
+	u8 data[2];
+	u8 sampleDivider;
+
+	sampleDivider = st->sample_divider;
+	sampleDivider++;
+
+	/* 60 ms minimum time added */
+	dmpTime = ((time) / sampleDivider);
+	data[0] = dmpTime >> 8;
+	data[1] = dmpTime & 0xFF;
+
+	result = mem_w_key(KEY_D_1_88, ARRAY_SIZE(data), data);
+	return result;
+}
+
+static int inv_set_shake_reject_timeout_dmp(struct inv_mpu_iio_s *st,
+						u32 time)
+{
+	/*How long the gyros must remain below their threshold,
+	after taps have been rejected, before taps can be detected again*/
+	int result;
+	/* DMP Algorithm */
+	u16 dmpTime;
+	u8 data[2];
+	u8 sampleDivider;
+
+	sampleDivider = st->sample_divider;
+	sampleDivider++;
+
+	/* 60 ms minimum time added */
+	dmpTime = ((time) / sampleDivider);
+	data[0] = dmpTime >> 8;
+	data[1] = dmpTime & 0xFF;
+
+	result = mem_w_key(KEY_D_1_90, ARRAY_SIZE(data), data);
+	return result;
+}
+
+int inv_set_interrupt_on_gesture_event(struct inv_mpu_iio_s *st, bool on)
+{
+	u8 result;
+	const u8 regs_on[] = {DINADA, DINADA, DINAB1, DINAB9,
+					 DINAF3, DINA8B, DINAA3, DINA91,
+					 DINAB6, DINADA, DINAB4, DINADA};
+	const u8 regs_off[] = {0xd8, 0xd8, 0xb1, 0xb9, 0xf3, 0x8b,
+					  0xa3, 0x91, 0xb6, 0x09, 0xb4, 0xd9};
+	/*For some reason DINAC4 is defined as 0xb8,
+	but DINBC4 is not defined.*/
+	const u8 regs_end[] = {DINAFE, DINAF2, DINAAB, 0xc4,
+					DINAAA, DINAF1, DINADF, DINADF};
+	if (on)
+		/*Sets the DMP to send an interrupt and put a FIFO packet
+		in the FIFO if and only if a tap/orientation event
+		just occurred*/
+		result = mem_w_key(KEY_CFG_FIFO_ON_EVENT, ARRAY_SIZE(regs_on),
+					regs_on);
+	else
+		/*Sets the DMP to send an interrupt and put a FIFO packet
+		in the FIFO at the rate specified by the FIFO div.
+		see inv_set_fifo_div in hw_setup.c to set the FIFO div.*/
+		result = mem_w_key(KEY_CFG_FIFO_ON_EVENT, ARRAY_SIZE(regs_off),
+					regs_off);
+	if (result)
+		return result;
+
+	result = mem_w_key(KEY_CFG_6, ARRAY_SIZE(regs_end), regs_end);
+	return result;
+}
+
+/**
+ * inv_enable_tap_dmp() -  calling this function will enable/disable tap function.
+ */
+int inv_enable_tap_dmp(struct inv_mpu_iio_s *st, bool on)
+{
+	int result;
+	result = inv_set_tap_interrupt_dmp(st, on);
+	if (result)
+		return result;
+	if (on) {
+		result = inv_set_tap_threshold_dmp(st, INV_TAP_AXIS_X,
+						   st->tap.thresh);
+		if (result)
+			return result;
+		result = inv_set_tap_threshold_dmp(st, INV_TAP_AXIS_Y,
+						   st->tap.thresh);
+		if (result)
+			return result;
+		result = inv_set_tap_threshold_dmp(st, INV_TAP_AXIS_Z,
+						   st->tap.thresh);
+		if (result)
+			return result;
+	}
+
+	result = inv_set_tap_axes_dmp(st, INV_TAP_ALL_DIRECTIONS);
+	if (result)
+		return result;
+	result = inv_set_min_taps_dmp(st, st->tap.min_count);
+	if (result)
+		return result;
+
+	result = inv_set_tap_time_dmp(st, st->tap.time);
+	if (result)
+		return result;
+
+	result = inv_set_multiple_tap_time_dmp(st, DMP_MULTI_TAP_TIME);
+	if (result)
+		return result;
+
+	result = inv_set_gyro_sf_dmp(st);
+	if (result)
+		return result;
+
+	result = inv_set_shake_reject_thresh_dmp(st, DMP_SHAKE_REJECT_THRESH);
+	if (result)
+		return result;
+
+	result = inv_set_shake_reject_time_dmp(st, DMP_SHAKE_REJECT_TIME);
+	if (result)
+		return result;
+
+	result = inv_set_shake_reject_timeout_dmp(st,
+						  DMP_SHAKE_REJECT_TIMEOUT);
+	return result;
+}
+
+static int inv_set_orientation_dmp(struct inv_mpu_iio_s *st,
+					int orientation)
+{
+	/*Set a mask in the DMP determining what orientations
+			will trigger interrupts*/
+	u8 regs[4];
+	u8 result;
+
+	/* check if any spurious bit other the ones expected are set */
+	if (orientation & (~(INV_ORIENTATION_ALL | INV_ORIENTATION_FLIP)))
+		return -EINVAL;
+
+	regs[0] = (u8)orientation;
+	result = mem_w_key(KEY_D_1_74, 1, regs);
+	return result;
+}
+
+static int inv_set_orientation_thresh_dmp(struct inv_mpu_iio_s *st,
+					int angle)
+{
+	/*Set an angle threshold in the DMP determining
+		when orientations change*/
+	u8 *regs;
+	u8 result;
+	u32 out;
+	u32 d;
+	const u32 threshold[] = {138952416, 268435455, 379625062,
+					  464943848, 518577479, 536870912};
+	/* The real calculation is
+	 * threshold = (long)((1 << 29) * sin((angle * M_PI) / 180.));
+	 * Here we have to use table lookup*/
+	d = angle / DMP_ANGLE_SCALE;
+	d -= 1;
+	if (d >= ARRAY_SIZE(threshold))
+		return -EPERM;
+	out = cpu_to_be32p(&threshold[d]);
+	regs = (u8 *)&out;
+
+	result = mem_w_key(KEY_D_1_232, sizeof(out), regs);
+	return result;
+}
+
+static int inv_set_orientation_time_dmp(struct inv_mpu_iio_s *st,
+					u32 time)
+{
+	/*Determines the stability time required before a
+	new orientation can be adopted */
+	u16 dmpTime;
+	u8 data[2];
+	u8 sampleDivider;
+	u8 result;
+	/* First check if we are allowed to call this function here */
+	sampleDivider = st->sample_divider;
+	sampleDivider++;
+	/* 60 ms minimum time added */
+	dmpTime = ((time) / sampleDivider);
+	data[0] = dmpTime >> 8;
+	data[1] = dmpTime & 0xFF;
+	result = mem_w_key(KEY_D_1_250, 2, data);
+
+	return result;
+}
+
+/**
+ * inv_enable_orientation_dmp() -  calling this function will
+ *                  enable/disable orientation function.
+ */
+int inv_enable_orientation_dmp(struct inv_mpu_iio_s *st, bool on)
+{
+	int result;
+	result = inv_set_orientation_interrupt_dmp(st, on);
+	if (result)
+		return result;
+	result = inv_set_orientation_dmp(st, 0x40 | INV_ORIENTATION_ALL);
+	if (result)
+		return result;
+	result = inv_set_gyro_sf_dmp(st);
+	if (result)
+		return result;
+	result = inv_set_orientation_thresh_dmp(st, DMP_ORIENTATION_ANGLE);
+	if (result)
+		return result;
+	result = inv_set_orientation_time_dmp(st, DMP_ORIENTATION_TIME);
+	return result;
+}
+
+static int inv_send_sensor_data(struct inv_mpu_iio_s *st,
+				u16 elements)
+{
+	int result;
+	u8 regs[] = {DINAA0 + 3, DINAA0 + 3, DINAA0 + 3,
+				DINAA0 + 3, DINAA0 + 3, DINAA0 + 3,
+				DINAA0 + 3, DINAA0 + 3, DINAA0 + 3,
+				DINAA0 + 3};
+
+	if (elements & INV_ELEMENT_1)
+		regs[0] = DINACA;
+	if (elements & INV_ELEMENT_2)
+		regs[4] = DINBC4;
+	if (elements & INV_ELEMENT_3)
+		regs[5] = DINACC;
+	if (elements & INV_ELEMENT_4)
+		regs[6] = DINBC6;
+	if ((elements & INV_ELEMENT_5) || (elements & INV_ELEMENT_6) ||
+	    (elements & INV_ELEMENT_7)) {
+		regs[1] = DINBC0;
+		regs[2] = DINAC8;
+		regs[3] = DINBC2;
+	}
+	result = mem_w_key(KEY_CFG_15, ARRAY_SIZE(regs), regs);
+	return result;
+}
+
+static int inv_send_interrupt_word(struct inv_mpu_iio_s *st)
+{
+	const u8 regs[] = { DINA20 };
+	u8 result;
+
+	result = mem_w_key(KEY_CFG_27, ARRAY_SIZE(regs), regs);
+	return result;
+}
+
+/**
+ * inv_dmp_firmware_write() -  calling this function will load the firmware.
+ *                        This is the write function of file "dmp_firmware".
+ */
+ssize_t inv_dmp_firmware_write(struct file *fp, struct kobject *kobj,
+	struct bin_attribute *attr,
+	char *buf, loff_t pos, size_t size)
+{
+	u8 *firmware;
+	int result;
+	uint32_t crc;
+	int crc_idx;
+	struct inv_reg_map_s *reg;
+	struct iio_dev *indio_dev;
+	struct inv_mpu_iio_s *st;
+
+	indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj));
+	st = iio_priv(indio_dev);
+
+	if (st->chip_config.is_asleep || st->chip_config.firmware_loaded)
+		return -EINVAL;
+
+	reg = &st->reg;
+	if (DMP_IMAGE_SIZE != size) {
+		pr_err("wrong DMP image size\n");
+		return -EINVAL;
+	}
+
+	firmware = kmalloc(size, GFP_KERNEL);
+	if (!firmware)
+		return -ENOMEM;
+
+	memcpy(firmware, buf, size);
+	crc = crc32(CRC_FIRMWARE_SEED, firmware, size);
+	for (crc_idx = 0; crc_idx < ARRAY_SIZE(DMP_IMAGE_CRC_VALUES); crc_idx++)
+		if (DMP_IMAGE_CRC_VALUES[crc_idx] == crc)
+			break;
+	if (crc_idx >= ARRAY_SIZE(DMP_IMAGE_CRC_VALUES)) {
+		pr_err("firmware CRC error. Got 0x%08x\n", crc);
+		result = -EINVAL;
+		goto firmware_write_fail;
+	}
+
+	result = inv_load_firmware(st, firmware, size);
+	if (result)
+		goto firmware_write_fail;
+
+	result = inv_verify_firmware(st, firmware, size);
+	if (result)
+		goto firmware_write_fail;
+
+	result = inv_i2c_single_write(st, reg->prgm_strt_addrh,
+	st->chip_config.prog_start_addr >> 8);
+	if (result)
+		goto firmware_write_fail;
+	result = inv_i2c_single_write(st, reg->prgm_strt_addrh + 1,
+	st->chip_config.prog_start_addr & 0xff);
+	if (result)
+		goto firmware_write_fail;
+
+	result = inv_verify_firmware(st, firmware, size);
+	if (result)
+		goto firmware_write_fail;
+	result = inv_set_fifo_rate(st, DMP_DEFAULT_FIFO_RATE);
+	if (result)
+		goto firmware_write_fail;
+	result = inv_send_sensor_data(st, INV_GYRO_ACC_MASK);
+	if (result)
+		goto firmware_write_fail;
+	result = inv_send_interrupt_word(st);
+	if (result)
+		goto firmware_write_fail;
+	result = inv_gyro_dmp_cal(st);
+	if (result)
+		goto firmware_write_fail;
+	result = inv_accel_dmp_cal(st);
+	if (result)
+		goto firmware_write_fail;
+	st->chip_config.firmware_loaded = 1;
+	pr_info("firmware loaded CRC=0x%08x\n", DMP_IMAGE_CRC_VALUES[crc_idx]);
+	result = size;
+firmware_write_fail:
+	kfree(firmware);
+
+	return result;
+}
+
+ssize_t inv_dmp_firmware_read(struct file *filp,
+				struct kobject *kobj,
+				struct bin_attribute *bin_attr,
+				char *buf, loff_t off, size_t count)
+{
+	int bank, write_size, size, data, result;
+	u16 memaddr;
+	struct iio_dev *indio_dev;
+	struct inv_mpu_iio_s *st;
+	size = count;
+
+	indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj));
+	st = iio_priv(indio_dev);
+	data = 0;
+	for (bank = 0; size > 0; bank++, size -= write_size,
+					data += write_size) {
+		if (size > MPU_MEM_BANK_SIZE)
+			write_size = MPU_MEM_BANK_SIZE;
+		else
+			write_size = size;
+
+		memaddr = (bank << 8);
+		result = mpu_memory_read(st->sl_handle,
+			st->i2c_addr, memaddr, write_size, &buf[data]);
+		if (result)
+			return result;
+	}
+
+	return 0;
+}
+/**
+ *  @}
+ */
+
diff --git a/drivers/staging/iio/imu/mpu/inv_mpu_ring.c b/drivers/staging/iio/imu/mpu/inv_mpu_ring.c
new file mode 100644
index 0000000..62d2bb0
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu/inv_mpu_ring.c
@@ -0,0 +1,830 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    inv_mpu_ring.c
+ *      @brief   A sysfs device driver for Invensense gyroscopes.
+ *      @details This file is part of inv mpu iio driver code
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include "inv_mpu_iio.h"
+#include "../../iio.h"
+#include "../../kfifo_buf.h"
+#include "../../trigger_consumer.h"
+#include "../../sysfs.h"
+
+static void inv_scan_query(struct iio_dev *indio_dev)
+{
+	struct inv_mpu_iio_s  *st = iio_priv(indio_dev);
+	struct iio_buffer *ring = indio_dev->buffer;
+
+	if (iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_GYRO_X) ||
+	    iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_GYRO_Y) ||
+	    iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_GYRO_Z))
+		st->chip_config.gyro_fifo_enable = 1;
+	else
+		st->chip_config.gyro_fifo_enable = 0;
+
+	if (iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_ACCL_X) ||
+	    iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_ACCL_Y) ||
+	    iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_ACCL_Z))
+		st->chip_config.accl_fifo_enable = 1;
+	else
+		st->chip_config.accl_fifo_enable = 0;
+
+	if (iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_MAGN_X) ||
+	    iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_MAGN_Y) ||
+	    iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_MAGN_Z))
+		st->chip_config.compass_fifo_enable = 1;
+	else
+		st->chip_config.compass_fifo_enable = 0;
+}
+
+/**
+ *  reset_fifo_mpu3050() - Reset FIFO related registers
+ *  @st:	Device driver instance.
+ */
+static int reset_fifo_mpu3050(struct iio_dev *indio_dev)
+{
+	struct inv_reg_map_s *reg;
+	int result;
+	unsigned char val, user_ctrl;
+	struct inv_mpu_iio_s  *st = iio_priv(indio_dev);
+	reg = &st->reg;
+
+	inv_scan_query(indio_dev);
+	/* disable interrupt */
+	result = inv_i2c_single_write(st, reg->int_enable,
+				st->plat_data.int_config);
+	if (result)
+		return result;
+	/* disable the sensor output to FIFO */
+	result = inv_i2c_single_write(st, reg->fifo_en, 0);
+	if (result)
+		goto reset_fifo_fail;
+	result = inv_i2c_read(st, reg->user_ctrl, 1, &user_ctrl);
+	if (result)
+		goto reset_fifo_fail;
+	/* disable fifo reading */
+	user_ctrl &= ~BIT_FIFO_EN;
+	st->chip_config.has_footer = 0;
+	/* reset fifo */
+	val = (BIT_3050_FIFO_RST | user_ctrl);
+	result = inv_i2c_single_write(st, reg->user_ctrl, val);
+	if (result)
+		goto reset_fifo_fail;
+	st->last_isr_time = get_time_ns();
+	if (st->chip_config.dmp_on) {
+		/* enable interrupt when DMP is done */
+		result = inv_i2c_single_write(st, reg->int_enable,
+				st->plat_data.int_config | BIT_DMP_INT_EN);
+		if (result)
+			return result;
+
+		result = inv_i2c_single_write(st, reg->user_ctrl,
+			BIT_FIFO_EN|user_ctrl);
+		if (result)
+			return result;
+	} else {
+		/* enable interrupt */
+		if (st->chip_config.accl_fifo_enable ||
+		    st->chip_config.gyro_fifo_enable) {
+			result = inv_i2c_single_write(st, reg->int_enable,
+				st->plat_data.int_config | BIT_DATA_RDY_EN);
+			if (result)
+				return result;
+		}
+		/* enable FIFO reading and I2C master interface*/
+		result = inv_i2c_single_write(st, reg->user_ctrl,
+			BIT_FIFO_EN | user_ctrl);
+		if (result)
+			return result;
+		/* enable sensor output to FIFO and FIFO footer*/
+		val = 1;
+		if (st->chip_config.accl_fifo_enable)
+			val |= BITS_3050_ACCL_OUT;
+		if (st->chip_config.gyro_fifo_enable)
+			val |= BITS_GYRO_OUT;
+		result = inv_i2c_single_write(st, reg->fifo_en, val);
+		if (result)
+			return result;
+	}
+
+	return 0;
+reset_fifo_fail:
+	if (st->chip_config.dmp_on)
+		val = BIT_DMP_INT_EN;
+	else
+		val = BIT_DATA_RDY_EN;
+	inv_i2c_single_write(st, reg->int_enable,
+			     st->plat_data.int_config | val);
+	pr_err("reset fifo failed\n");
+
+	return result;
+}
+
+/**
+ *  reset_fifo_itg() - Reset FIFO related registers.
+ *  @st:	Device driver instance.
+ */
+static int reset_fifo_itg(struct iio_dev *indio_dev)
+{
+	struct inv_reg_map_s *reg;
+	int result, data;
+	unsigned char val;
+	struct inv_mpu_iio_s  *st = iio_priv(indio_dev);
+	reg = &st->reg;
+
+	inv_scan_query(indio_dev);
+	/* disable interrupt */
+	result = inv_i2c_single_write(st, reg->int_enable, 0);
+	if (result) {
+		pr_err("int_enable write failed\n");
+		return result;
+	}
+	/* disable the sensor output to FIFO */
+	result = inv_i2c_single_write(st, reg->fifo_en, 0);
+	if (result)
+		goto reset_fifo_fail;
+	/* disable fifo reading */
+	result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+	if (result)
+		goto reset_fifo_fail;
+
+	if (st->chip_config.dmp_on) {
+		val = (BIT_FIFO_RST | BIT_DMP_RST);
+		result = inv_i2c_single_write(st, reg->user_ctrl, val);
+		if (result)
+			goto reset_fifo_fail;
+		st->last_isr_time = get_time_ns();
+		if (st->chip_config.dmp_int_on) {
+			result = inv_i2c_single_write(st, reg->int_enable,
+							BIT_DMP_INT_EN);
+			if (result)
+				return result;
+		}
+		val = (BIT_DMP_EN | BIT_FIFO_EN);
+		if (st->chip_config.compass_enable &
+			(!st->chip_config.dmp_event_int_on))
+			val |= BIT_I2C_MST_EN;
+		result = inv_i2c_single_write(st, reg->user_ctrl, val);
+		if (result)
+			goto reset_fifo_fail;
+
+		if (st->chip_config.compass_enable) {
+			/* I2C_MST_DLY is set according to sample rate,
+			   slow down the power*/
+			data = st->chip_config.fifo_rate /
+				st->chip_config.dmp_output_rate;
+			if (data > 0)
+				data -= 1;
+			result = inv_i2c_single_write(st, REG_I2C_SLV4_CTRL,
+							data);
+			if (result)
+				return result;
+		}
+	} else {
+		/* reset FIFO and possibly reset I2C*/
+		val = BIT_FIFO_RST;
+		result = inv_i2c_single_write(st, reg->user_ctrl, val);
+		if (result)
+			goto reset_fifo_fail;
+		st->last_isr_time = get_time_ns();
+		/* enable interrupt */
+		if (st->chip_config.accl_fifo_enable ||
+		    st->chip_config.gyro_fifo_enable ||
+		    st->chip_config.compass_enable) {
+			result = inv_i2c_single_write(st, reg->int_enable,
+						BIT_DATA_RDY_EN);
+			if (result)
+				return result;
+		}
+		/* enable FIFO reading and I2C master interface*/
+		val = BIT_FIFO_EN;
+		if (st->chip_config.compass_enable)
+			val |= BIT_I2C_MST_EN;
+		result = inv_i2c_single_write(st, reg->user_ctrl, val);
+		if (result)
+			goto reset_fifo_fail;
+		if (st->chip_config.compass_enable) {
+			/* I2C_MST_DLY is set according to sample rate,
+			   slow down the power*/
+			data = COMPASS_RATE_SCALE *
+				st->chip_config.fifo_rate / ONE_K_HZ;
+			if (data > 0)
+				data -= 1;
+			result = inv_i2c_single_write(st, REG_I2C_SLV4_CTRL,
+							data);
+			if (result)
+				return result;
+		}
+		/* enable sensor output to FIFO */
+		val = 0;
+		if (st->chip_config.gyro_fifo_enable)
+			val |= BITS_GYRO_OUT;
+		if (st->chip_config.accl_fifo_enable)
+			val |= BIT_ACCEL_OUT;
+		result = inv_i2c_single_write(st, reg->fifo_en, val);
+		if (result)
+			goto reset_fifo_fail;
+	}
+	return 0;
+reset_fifo_fail:
+	if (st->chip_config.dmp_on)
+		val = BIT_DMP_INT_EN;
+	else
+		val = BIT_DATA_RDY_EN;
+	inv_i2c_single_write(st, reg->int_enable, val);
+	pr_err("reset fifo failed\n");
+
+	return result;
+}
+
+/**
+ *  inv_reset_fifo() - Reset FIFO related registers.
+ *  @st:	Device driver instance.
+ */
+static int inv_reset_fifo(struct iio_dev *indio_dev)
+{
+	struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+	if (INV_MPU3050 == st->chip_type)
+		return reset_fifo_mpu3050(indio_dev);
+	else
+		return reset_fifo_itg(indio_dev);
+}
+
+/**
+ *  set_inv_enable() - Reset FIFO related registers.
+ *			This also powers on the chip if needed.
+ *  @st:	Device driver instance.
+ *  @fifo_enable: enable/disable
+ */
+int set_inv_enable(struct iio_dev *indio_dev,
+			bool enable) {
+	struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+	struct inv_reg_map_s *reg;
+	int result;
+
+	if (st->chip_config.is_asleep)
+		return -EINVAL;
+	reg = &st->reg;
+	if (enable) {
+		result = inv_reset_fifo(indio_dev);
+		if (result)
+			return result;
+	} else {
+		result = inv_i2c_single_write(st, reg->fifo_en, 0);
+		if (result)
+			return result;
+		/* disable fifo reading */
+		if (INV_MPU3050 != st->chip_type) {
+			result = inv_i2c_single_write(st, reg->int_enable, 0);
+			if (result)
+				return result;
+			result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+		} else {
+			result = inv_i2c_single_write(st, reg->int_enable,
+				st->plat_data.int_config);
+		}
+		if (result)
+			return result;
+	}
+	st->chip_config.enable = !!enable;
+
+	return 0;
+}
+
+/**
+ *  inv_clear_kfifo() - clear time stamp fifo
+ *  @st:	Device driver instance.
+ */
+void inv_clear_kfifo(struct inv_mpu_iio_s *st)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&st->time_stamp_lock, flags);
+	kfifo_reset(&st->timestamps);
+	spin_unlock_irqrestore(&st->time_stamp_lock, flags);
+}
+
+/**
+ *  inv_irq_handler() - Cache a timestamp at each data ready interrupt.
+ */
+static irqreturn_t inv_irq_handler(int irq, void *dev_id)
+{
+	struct inv_mpu_iio_s *st;
+	long long timestamp;
+	int catch_up;
+	long long time_since_last_irq;
+
+	st = (struct inv_mpu_iio_s *)dev_id;
+	timestamp = get_time_ns();
+	time_since_last_irq = timestamp - st->last_isr_time;
+	spin_lock(&st->time_stamp_lock);
+	catch_up = 0;
+	while ((time_since_last_irq > st->irq_dur_ns * 2) &&
+	       (catch_up < MAX_CATCH_UP) &&
+	       (!st->chip_config.lpa_mode) &&
+	       (!st->chip_config.dmp_on)) {
+		st->last_isr_time += st->irq_dur_ns;
+		kfifo_in(&st->timestamps,
+			 &st->last_isr_time, 1);
+		time_since_last_irq = timestamp - st->last_isr_time;
+		catch_up++;
+	}
+	kfifo_in(&st->timestamps, &timestamp, 1);
+	st->last_isr_time = timestamp;
+	spin_unlock(&st->time_stamp_lock);
+
+	return IRQ_WAKE_THREAD;
+}
+
+static int put_scan_to_buf(struct iio_dev *indio_dev, unsigned char *d,
+				short *s, int scan_index, int d_ind) {
+	struct iio_buffer *ring = indio_dev->buffer;
+	int st;
+	int i;
+	for (i = 0; i < 3; i++) {
+		st = iio_scan_mask_query(indio_dev, ring, scan_index + i);
+		if (st) {
+			memcpy(&d[d_ind], &s[i], sizeof(s[i]));
+			d_ind += sizeof(s[i]);
+		}
+	}
+	return d_ind;
+}
+
+static int put_scan_to_buf_q(struct iio_dev *indio_dev, unsigned char *d,
+				int *s, int scan_index, int d_ind) {
+	struct iio_buffer *ring = indio_dev->buffer;
+	int st;
+	int i;
+	for (i = 0; i < 4; i++) {
+		st = iio_scan_mask_query(indio_dev, ring, scan_index + i);
+		if (st) {
+			memcpy(&d[d_ind], &s[i], sizeof(s[i]));
+			d_ind += sizeof(s[i]);
+		}
+	}
+	return d_ind;
+}
+
+static void inv_report_data_3050(struct iio_dev *indio_dev, s64 t,
+			int has_footer, unsigned char *data)
+{
+	struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+	struct iio_buffer *ring = indio_dev->buffer;
+	int ind, i, d_ind;
+	struct inv_chip_config_s *conf;
+	short g[THREE_AXIS], a[THREE_AXIS];
+	s64 buf[8];
+	unsigned char *tmp;
+	int bytes_per_datum, scan_count;
+	conf = &st->chip_config;
+
+	scan_count = bitmap_weight(indio_dev->active_scan_mask,
+				       indio_dev->masklength);
+	bytes_per_datum = scan_count * 2;
+
+	ind = 0;
+	if (has_footer)
+		ind += 2;
+	tmp = (unsigned char *)buf;
+	d_ind = 0;
+	if (conf->gyro_fifo_enable) {
+		g[0] = be16_to_cpup((__be16 *)(&data[ind]));
+		g[1] = be16_to_cpup((__be16 *)(&data[ind + 2]));
+		g[2] = be16_to_cpup((__be16 *)(&data[ind + 4]));
+		ind += BYTES_PER_SENSOR;
+		d_ind = put_scan_to_buf(indio_dev, tmp, g,
+			INV_MPU_SCAN_GYRO_X, d_ind);
+	}
+	if (conf->accl_fifo_enable) {
+		st->mpu_slave->combine_data(&data[ind], a);
+		ind += BYTES_PER_SENSOR;
+		d_ind = put_scan_to_buf(indio_dev, tmp, a,
+			INV_MPU_SCAN_ACCL_X, d_ind);
+	}
+
+	i = (bytes_per_datum + 7) / 8;
+	if (ring->scan_timestamp)
+		buf[i] = t;
+	ring->access->store_to(indio_dev->buffer, (u8 *)buf, t);
+}
+
+/**
+ *  inv_read_fifo_mpu3050() - Transfer data from FIFO to ring buffer for mpu3050.
+ */
+irqreturn_t inv_read_fifo_mpu3050(int irq, void *dev_id)
+{
+
+	struct inv_mpu_iio_s *st = (struct inv_mpu_iio_s *)dev_id;
+	struct iio_dev *indio_dev = iio_priv_to_dev(st);
+	int bytes_per_datum;
+	unsigned char data[64];
+	int result;
+	short fifo_count, byte_read;
+	unsigned int copied;
+	s64 timestamp;
+	struct inv_reg_map_s *reg;
+	reg = &st->reg;
+	/* It is impossible that chip is asleep or enable is
+	zero when interrupt is on
+	*  because interrupt is now connected with enable */
+	if (st->chip_config.dmp_on)
+		bytes_per_datum = BYTES_FOR_DMP;
+	else
+		bytes_per_datum = (st->chip_config.accl_fifo_enable +
+			st->chip_config.gyro_fifo_enable)*BYTES_PER_SENSOR;
+	if (st->chip_config.has_footer)
+		byte_read = bytes_per_datum + MPU3050_FOOTER_SIZE;
+	else
+		byte_read = bytes_per_datum;
+
+	fifo_count = 0;
+	if (byte_read != 0) {
+		result = inv_i2c_read(st, reg->fifo_count_h,
+				FIFO_COUNT_BYTE, data);
+		if (result)
+			goto end_session;
+		fifo_count = be16_to_cpup((__be16 *)(&data[0]));
+		if (fifo_count < byte_read)
+			goto end_session;
+		if (fifo_count & 1)
+			goto flush_fifo;
+		if (fifo_count > FIFO_THRESHOLD)
+			goto flush_fifo;
+		/* Timestamp mismatch. */
+		if (kfifo_len(&st->timestamps) <
+			fifo_count / byte_read)
+			goto flush_fifo;
+		if (kfifo_len(&st->timestamps) >
+			fifo_count / byte_read + TIME_STAMP_TOR) {
+			if (st->chip_config.dmp_on) {
+				result = kfifo_to_user(&st->timestamps,
+				&timestamp, sizeof(timestamp), &copied);
+				if (result)
+					goto flush_fifo;
+			} else {
+				goto flush_fifo;
+			}
+		}
+	}
+	while ((bytes_per_datum != 0) && (fifo_count >= byte_read)) {
+		result = inv_i2c_read(st, reg->fifo_r_w, byte_read, data);
+		if (result)
+			goto flush_fifo;
+
+		result = kfifo_to_user(&st->timestamps,
+			&timestamp, sizeof(timestamp), &copied);
+		if (result)
+			goto flush_fifo;
+		inv_report_data_3050(indio_dev, timestamp,
+				     st->chip_config.has_footer, data);
+		fifo_count -= byte_read;
+		if (st->chip_config.has_footer == 0) {
+			st->chip_config.has_footer = 1;
+			byte_read = bytes_per_datum + MPU3050_FOOTER_SIZE;
+		}
+	}
+end_session:
+	return IRQ_HANDLED;
+flush_fifo:
+	/* Flush HW and SW FIFOs. */
+	inv_reset_fifo(indio_dev);
+	inv_clear_kfifo(st);
+	return IRQ_HANDLED;
+}
+
+static int inv_report_gyro_accl_compass(struct iio_dev *indio_dev,
+					unsigned char *data, s64 t)
+{
+	struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+	struct iio_buffer *ring = indio_dev->buffer;
+	short g[THREE_AXIS], a[THREE_AXIS], c[THREE_AXIS];
+	int q[4];
+	int result, ind, d_ind;
+	s64 buf[8];
+	u32 word;
+	u8 d[8], compass_divider;
+	u8 *tmp;
+	int source;
+	struct inv_chip_config_s *conf;
+
+	conf = &st->chip_config;
+	ind = 0;
+	if (conf->quaternion_on & conf->dmp_on) {
+		q[0] = be32_to_cpup((__be32 *)(&data[ind]));
+		q[1] = be32_to_cpup((__be32 *)(&data[ind + 4]));
+		q[2] = be32_to_cpup((__be32 *)(&data[ind + 8]));
+		q[3] = be32_to_cpup((__be32 *)(&data[ind + 12]));
+		st->raw_quaternion[0] = q[0];
+		st->raw_quaternion[1] = q[1];
+		st->raw_quaternion[2] = q[2];
+		st->raw_quaternion[3] = q[3];
+		ind += QUATERNION_BYTES;
+	}
+	if (conf->accl_fifo_enable | conf->dmp_on) {
+		a[0] = be16_to_cpup((__be16 *)(&data[ind]));
+		a[1] = be16_to_cpup((__be16 *)(&data[ind + 2]));
+		a[2] = be16_to_cpup((__be16 *)(&data[ind + 4]));
+
+		a[0] *= st->chip_info.multi;
+		a[1] *= st->chip_info.multi;
+		a[2] *= st->chip_info.multi;
+		st->raw_accel[0] = a[0];
+		st->raw_accel[1] = a[1];
+		st->raw_accel[2] = a[2];
+		ind += BYTES_PER_SENSOR;
+	}
+	if (conf->gyro_fifo_enable | conf->dmp_on) {
+		g[0] = be16_to_cpup((__be16 *)(&data[ind]));
+		g[1] = be16_to_cpup((__be16 *)(&data[ind + 2]));
+		g[2] = be16_to_cpup((__be16 *)(&data[ind + 4]));
+
+		st->raw_gyro[0] = g[0];
+		st->raw_gyro[1] = g[1];
+		st->raw_gyro[2] = g[2];
+		ind += BYTES_PER_SENSOR;
+	}
+	if (conf->dmp_on) {
+		word = (unsigned int)(be32_to_cpup((unsigned int *)&data[ind]));
+		source = ((word >> 16) & 0xff);
+		if (source) {
+			st->tap_data = (DMP_MASK_TAP & (word & 0xff));
+			st->orient_data = ((word >> 8) & 0xff);
+			st->display_orient_data =
+			((DMP_MASK_DIS_ORIEN & (word & 0xff)) >>
+			  DMP_DIS_ORIEN_SHIFT);
+		}
+
+		/* report tap information */
+		if (source & INT_SRC_TAP)
+			sysfs_notify(&indio_dev->dev.kobj, NULL, "event_tap");
+		/* report orientation information */
+		if (source & INT_SRC_ORIENT)
+			sysfs_notify(&indio_dev->dev.kobj, NULL,
+				     "event_orientation");
+		/* report orientation information */
+		if (source & INT_SRC_DISPLAY_ORIENT)
+			sysfs_notify(&indio_dev->dev.kobj, NULL,
+				     "event_display_orientation");
+	}
+	/*divider and counter is used to decrease the speed of read in
+		high frequency sample rate*/
+	if (conf->compass_fifo_enable) {
+		c[0] = 0;
+		c[1] = 0;
+		c[2] = 0;
+		if (conf->dmp_on)
+			compass_divider = st->compass_dmp_divider;
+		else
+			compass_divider = st->compass_divider;
+		if (compass_divider == st->compass_counter) {
+			/*read from external sensor data register */
+			result = inv_i2c_read(st, REG_EXT_SENS_DATA_00,
+					      NUM_BYTES_COMPASS_SLAVE, d);
+			/* d[7] is status 2 register */
+			/*for AKM8975, bit 2 and 3 should be all be zero*/
+			/* for AMK8963, bit 3 should be zero*/
+			if ((DATA_AKM_DRDY == d[0]) &&
+			    (0 == (d[7] & DATA_AKM_STAT_MASK)) &&
+			    (!result)) {
+				u8 *sens;
+				sens = st->chip_info.compass_sens;
+				c[0] = (short)((d[2] << 8) | d[1]);
+				c[1] = (short)((d[4] << 8) | d[3]);
+				c[2] = (short)((d[6] << 8) | d[5]);
+				c[0] = (short)(((int)c[0] *
+					       (sens[0] + 128)) >> 8);
+				c[1] = (short)(((int)c[1] *
+					       (sens[1] + 128)) >> 8);
+				c[2] = (short)(((int)c[2] *
+					       (sens[2] + 128)) >> 8);
+				st->raw_compass[0] = c[0];
+				st->raw_compass[1] = c[1];
+				st->raw_compass[2] = c[2];
+			}
+			st->compass_counter = 0;
+		} else if (compass_divider != 0) {
+			st->compass_counter++;
+		}
+	}
+
+	tmp = (unsigned char *)buf;
+	d_ind = 0;
+	if (conf->quaternion_on & conf->dmp_on)
+		d_ind = put_scan_to_buf_q(indio_dev, tmp, q,
+				INV_MPU_SCAN_QUAT_R, d_ind);
+	if (conf->gyro_fifo_enable)
+		d_ind = put_scan_to_buf(indio_dev, tmp, g,
+				INV_MPU_SCAN_GYRO_X, d_ind);
+	if (conf->accl_fifo_enable)
+		d_ind = put_scan_to_buf(indio_dev, tmp, a,
+				INV_MPU_SCAN_ACCL_X, d_ind);
+	if (conf->compass_fifo_enable)
+		d_ind = put_scan_to_buf(indio_dev, tmp, c,
+				INV_MPU_SCAN_MAGN_X, d_ind);
+	if (ring->scan_timestamp)
+		buf[(d_ind + 7) / 8] = t;
+	ring->access->store_to(indio_dev->buffer, (u8 *)buf, t);
+
+	return 0;
+}
+
+/**
+ *  inv_read_fifo() - Transfer data from FIFO to ring buffer.
+ */
+irqreturn_t inv_read_fifo(int irq, void *dev_id)
+{
+
+	struct inv_mpu_iio_s *st = (struct inv_mpu_iio_s *)dev_id;
+	struct iio_dev *indio_dev = iio_priv_to_dev(st);
+	size_t bytes_per_datum;
+	int result;
+	unsigned char data[BYTES_FOR_DMP + QUATERNION_BYTES];
+	unsigned short fifo_count;
+	unsigned int copied;
+	s64 timestamp;
+	struct inv_reg_map_s *reg;
+	s64 buf[8];
+	unsigned char *tmp;
+	reg = &st->reg;
+	if (!(st->chip_config.accl_fifo_enable |
+		st->chip_config.gyro_fifo_enable |
+		st->chip_config.dmp_on |
+		st->chip_config.compass_fifo_enable))
+		goto end_session;
+	if (st->chip_config.dmp_on && st->chip_config.flick_int_on) {
+		/*dmp interrupt status */
+		inv_i2c_read(st, REG_DMP_INT_STATUS, 1, data);
+		if (data[0] & FLICK_INT_STATUS)
+			sysfs_notify(&indio_dev->dev.kobj, NULL, "event_flick");
+	}
+	if (st->chip_config.lpa_mode) {
+		result = inv_i2c_read(st, reg->raw_accl,
+				      BYTES_PER_SENSOR, data);
+		if (result)
+			goto end_session;
+		inv_report_gyro_accl_compass(indio_dev, data,
+					     get_time_ns());
+		goto end_session;
+	}
+
+	if (st->chip_config.dmp_on)
+		if (st->chip_config.quaternion_on)
+			bytes_per_datum = BYTES_FOR_DMP + QUATERNION_BYTES;
+		else
+			bytes_per_datum = BYTES_FOR_DMP;
+	else
+		bytes_per_datum = (st->chip_config.accl_fifo_enable +
+		st->chip_config.gyro_fifo_enable)*BYTES_PER_SENSOR;
+	fifo_count = 0;
+	if (bytes_per_datum != 0) {
+		result = inv_i2c_read(st, reg->fifo_count_h,
+				FIFO_COUNT_BYTE, data);
+		if (result)
+			goto end_session;
+		fifo_count = be16_to_cpup((__be16 *)(&data[0]));
+		if (fifo_count < bytes_per_datum)
+			goto end_session;
+		/* fifo count can't be odd number */
+		if (fifo_count & 1)
+			goto flush_fifo;
+		if (fifo_count >  FIFO_THRESHOLD)
+			goto flush_fifo;
+		/* Timestamp mismatch. */
+		if (kfifo_len(&st->timestamps) <
+			fifo_count / bytes_per_datum)
+			goto flush_fifo;
+		if (kfifo_len(&st->timestamps) >
+			fifo_count / bytes_per_datum + TIME_STAMP_TOR) {
+			if (st->chip_config.dmp_on) {
+				result = kfifo_to_user(&st->timestamps,
+				&timestamp, sizeof(timestamp), &copied);
+				if (result)
+					goto flush_fifo;
+			} else {
+				goto flush_fifo;
+			}
+		}
+	} else {
+		result = kfifo_to_user(&st->timestamps,
+			&timestamp, sizeof(timestamp), &copied);
+		if (result)
+			goto flush_fifo;
+	}
+	tmp = (char *)buf;
+	while ((bytes_per_datum != 0) && (fifo_count >= bytes_per_datum)) {
+		result = inv_i2c_read(st, reg->fifo_r_w, bytes_per_datum,
+			data);
+		if (result)
+			goto flush_fifo;
+
+		result = kfifo_to_user(&st->timestamps,
+			&timestamp, sizeof(timestamp), &copied);
+		if (result)
+			goto flush_fifo;
+		inv_report_gyro_accl_compass(indio_dev, data, timestamp);
+		fifo_count -= bytes_per_datum;
+	}
+	if (bytes_per_datum == 0)
+		inv_report_gyro_accl_compass(indio_dev, data, timestamp);
+end_session:
+	return IRQ_HANDLED;
+flush_fifo:
+	/* Flush HW and SW FIFOs. */
+	inv_reset_fifo(indio_dev);
+	inv_clear_kfifo(st);
+	return IRQ_HANDLED;
+}
+
+void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev)
+{
+	struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+	free_irq(st->client->irq, st);
+	iio_kfifo_free(indio_dev->buffer);
+};
+
+int inv_postenable(struct iio_dev *indio_dev)
+{
+	return set_inv_enable(indio_dev, true);
+
+}
+
+int inv_predisable(struct iio_dev *indio_dev)
+{
+	return set_inv_enable(indio_dev, false);
+}
+
+static const struct iio_buffer_setup_ops inv_mpu_ring_setup_ops = {
+	.preenable = &iio_sw_buffer_preenable,
+	.postenable = &inv_postenable,
+	.predisable = &inv_predisable,
+};
+
+int inv_mpu_configure_ring(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+	struct iio_buffer *ring;
+
+	ring = iio_kfifo_allocate(indio_dev);
+	if (!ring)
+		return -ENOMEM;
+	indio_dev->buffer = ring;
+	/* setup ring buffer */
+	ring->scan_timestamp = true;
+	indio_dev->setup_ops = &inv_mpu_ring_setup_ops;
+	/*scan count double count timestamp. should subtract 1. but
+	number of channels still includes timestamp*/
+	if (INV_MPU3050 == st->chip_type)
+		ret = request_threaded_irq(st->client->irq, inv_irq_handler,
+			inv_read_fifo_mpu3050,
+			IRQF_TRIGGER_RISING | IRQF_SHARED, "inv_irq", st);
+	else
+		ret = request_threaded_irq(st->client->irq, inv_irq_handler,
+			inv_read_fifo,
+			IRQF_TRIGGER_RISING | IRQF_SHARED, "inv_irq", st);
+	if (ret)
+		goto error_iio_sw_rb_free;
+
+	indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+	return 0;
+error_iio_sw_rb_free:
+	iio_kfifo_free(indio_dev->buffer);
+	return ret;
+}
+/**
+ *  @}
+ */
+
diff --git a/drivers/staging/iio/imu/mpu/inv_mpu_trigger.c b/drivers/staging/iio/imu/mpu/inv_mpu_trigger.c
new file mode 100644
index 0000000..0726dc3
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu/inv_mpu_trigger.c
@@ -0,0 +1,96 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    inv_mpu_trigger.c
+ *      @brief   A sysfs device driver for Invensense devices
+ *      @details This file is part of inv mpu iio driver code
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "../../iio.h"
+#include "../../sysfs.h"
+#include "../../trigger.h"
+#include "inv_mpu_iio.h"
+
+/**
+ * inv_mpu_data_rdy_trigger_set_state() set datardy interrupt state
+ **/
+static int inv_mpu_data_rdy_trigger_set_state(struct iio_trigger *trig,
+						bool state)
+{
+	struct iio_dev *indio_dev = trig->private_data;
+
+	dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
+	return set_inv_enable(indio_dev, state);
+}
+
+static const struct iio_trigger_ops inv_mpu_trigger_ops = {
+	.owner = THIS_MODULE,
+	.set_trigger_state = &inv_mpu_data_rdy_trigger_set_state,
+};
+
+int inv_mpu_probe_trigger(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+
+	st->trig = iio_allocate_trigger("%s-dev%d",
+					indio_dev->name,
+					indio_dev->id);
+	if (st->trig == NULL)
+		return -ENOMEM;
+	st->trig->dev.parent = &st->client->dev;
+	st->trig->private_data = indio_dev;
+	st->trig->ops = &inv_mpu_trigger_ops;
+	ret = iio_trigger_register(st->trig);
+
+	if (ret) {
+		iio_free_trigger(st->trig);
+		return -EPERM;
+	}
+	indio_dev->trig = st->trig;
+
+	return 0;
+}
+
+void inv_mpu_remove_trigger(struct iio_dev *indio_dev)
+{
+	struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+
+	iio_trigger_unregister(st->trig);
+	iio_free_trigger(st->trig);
+}
+/**
+ *  @}
+ */
+
diff --git a/drivers/staging/iio/imu/mpu/inv_slave_bma250.c b/drivers/staging/iio/imu/mpu/inv_slave_bma250.c
new file mode 100644
index 0000000..e836ddb
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu/inv_slave_bma250.c
@@ -0,0 +1,330 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    inv_slave_bma250.c
+ *      @brief   A sysfs device driver for Invensense devices
+ *      @details This file is part of invensense mpu driver code
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_mpu_iio.h"
+#define BMA250_CHIP_ID			3
+#define BMA250_RANGE_SET		0
+#define BMA250_BW_SET			4
+
+/* range and bandwidth */
+#define BMA250_RANGE_2G                 3
+#define BMA250_RANGE_4G                 5
+#define BMA250_RANGE_8G                 8
+#define BMA250_RANGE_16G                12
+#define BMA250_RANGE_MAX                4
+#define BMA250_RANGE_MASK               0xF0
+
+#define BMA250_BW_7_81HZ        0x08
+#define BMA250_BW_15_63HZ       0x09
+#define BMA250_BW_31_25HZ       0x0A
+#define BMA250_BW_62_50HZ       0x0B
+#define BMA250_BW_125HZ         0x0C
+#define BMA250_BW_250HZ         0x0D
+#define BMA250_BW_500HZ         0x0E
+#define BMA250_BW_1000HZ        0x0F
+#define BMA250_MAX_BW_SIZE      8
+#define BMA250_BW_REG_MASK      0xE0
+
+/*      register definitions */
+#define BMA250_X_AXIS_LSB_REG                   0x02
+#define BMA250_RANGE_SEL_REG                    0x0F
+#define BMA250_BW_SEL_REG                       0x10
+#define BMA250_MODE_CTRL_REG                    0x11
+
+/* mode settings */
+#define BMA250_MODE_NORMAL     0
+#define BMA250_MODE_LOWPOWER   1
+#define BMA250_MODE_SUSPEND    2
+#define BMA250_MODE_MAX        3
+#define BMA250_MODE_MASK       0x3F
+#define BMA250_BIT_SUSPEND     0x80
+#define BMA250_BIT_LP          0x40
+
+struct bma_property {
+	int range;
+	int bandwidth;
+	int mode;
+};
+
+static struct bma_property bma_static_property = {
+	.range = BMA250_RANGE_SET,
+	.bandwidth = BMA250_BW_SET,
+	.mode = BMA250_MODE_SUSPEND
+};
+
+static int bma250_set_bandwidth(struct inv_mpu_iio_s *st, unsigned char bw)
+{
+	int res;
+	unsigned char data;
+	int bandwidth;
+	switch (bw) {
+	case 0:
+		bandwidth = BMA250_BW_7_81HZ;
+		break;
+	case 1:
+		bandwidth = BMA250_BW_15_63HZ;
+		break;
+	case 2:
+		bandwidth = BMA250_BW_31_25HZ;
+		break;
+	case 3:
+		bandwidth = BMA250_BW_62_50HZ;
+		break;
+	case 4:
+		bandwidth = BMA250_BW_125HZ;
+		break;
+	case 5:
+		bandwidth = BMA250_BW_250HZ;
+		break;
+	case 6:
+		bandwidth = BMA250_BW_500HZ;
+		break;
+	case 7:
+		bandwidth = BMA250_BW_1000HZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+	res = inv_secondary_read(BMA250_BW_SEL_REG, 1, &data);
+	if (res)
+		return res;
+	data &= BMA250_BW_REG_MASK;
+	data |= bandwidth;
+	res = inv_secondary_write(BMA250_BW_SEL_REG, data);
+	return res;
+}
+
+static int bma250_set_range(struct inv_mpu_iio_s *st, unsigned char range)
+{
+	int res;
+	unsigned char orig, data;
+	switch (range) {
+	case 0:
+		data  = BMA250_RANGE_2G;
+		break;
+	case 1:
+		data  = BMA250_RANGE_4G;
+		break;
+	case 2:
+		data  = BMA250_RANGE_8G;
+		break;
+	case 3:
+		data  = BMA250_RANGE_16G;
+		break;
+	default:
+		return -EINVAL;
+	}
+	res = inv_secondary_read(BMA250_RANGE_SEL_REG, 1, &orig);
+	if (res)
+		return res;
+	orig &= BMA250_RANGE_MASK;
+	data |= orig;
+	res = inv_secondary_write(BMA250_RANGE_SEL_REG, data);
+	if (res)
+		return res;
+	bma_static_property.range = range;
+
+	return 0;
+}
+
+static int setup_slave_bma250(struct inv_mpu_iio_s *st)
+{
+	int result;
+	unsigned char data[2];
+	result = set_3050_bypass(st, true);
+	if (result)
+		return result;
+	/*read secondary i2c ID register */
+	result = inv_secondary_read(0, 1, data);
+	if (result)
+		return result;
+	if (BMA250_CHIP_ID != data[0])
+		return -EINVAL;
+	result = set_3050_bypass(st, false);
+	if (result)
+		return result;
+	/*AUX(accel), slave address is set inside set_3050_bypass*/
+	/* bma250 x axis LSB register address is 2 */
+	result = inv_i2c_single_write(st, REG_3050_AUX_BST_ADDR,
+					BMA250_X_AXIS_LSB_REG);
+
+	return result;
+}
+
+static int bma250_set_mode(struct inv_mpu_iio_s *st, unsigned char mode)
+{
+	int res;
+	unsigned char data;
+
+	res = inv_secondary_read(BMA250_MODE_CTRL_REG, 1, &data);
+	if (res)
+		return res;
+	data &= BMA250_MODE_MASK;
+	switch (mode) {
+	case BMA250_MODE_NORMAL:
+		break;
+	case BMA250_MODE_LOWPOWER:
+		data |= BMA250_BIT_LP;
+		break;
+	case BMA250_MODE_SUSPEND:
+		data |= BMA250_BIT_SUSPEND;
+		break;
+	default:
+		return -EINVAL;
+	}
+	res = inv_secondary_write(BMA250_MODE_CTRL_REG, data);
+	if (res)
+		return res;
+	bma_static_property.mode = mode;
+
+	return 0;
+}
+
+static int suspend_slave_bma250(struct inv_mpu_iio_s *st)
+{
+	int result;
+	if (bma_static_property.mode == BMA250_MODE_SUSPEND)
+		return 0;
+	/*set to bypass mode */
+	result = set_3050_bypass(st, true);
+	if (result)
+		return result;
+	bma250_set_mode(st, BMA250_MODE_SUSPEND);
+	/* no need to recover to non-bypass mode because we need it now */
+
+	return 0;
+}
+
+static int resume_slave_bma250(struct inv_mpu_iio_s *st)
+{
+	int result;
+	if (bma_static_property.mode == BMA250_MODE_NORMAL)
+		return 0;
+	/*set to bypass mode */
+	result = set_3050_bypass(st, true);
+	if (result)
+		return result;
+	result = bma250_set_mode(st, BMA250_MODE_NORMAL);
+	/* recover bypass mode */
+	result |= set_3050_bypass(st, false);
+
+	return result ? (-EINVAL) : 0;
+}
+
+static int combine_data_slave_bma250(unsigned char *in, short *out)
+{
+	out[0] = le16_to_cpup((__le16 *)(&in[0]));
+	out[1] = le16_to_cpup((__le16 *)(&in[2]));
+	out[2] = le16_to_cpup((__le16 *)(&in[4]));
+
+	return 0;
+}
+
+static int get_mode_slave_bma250(void)
+{
+	switch (bma_static_property.mode) {
+	case BMA250_MODE_SUSPEND:
+		return INV_MODE_SUSPEND;
+	case BMA250_MODE_NORMAL:
+		return INV_MODE_NORMAL;
+	default:
+		return -EINVAL;
+	}
+}
+
+/**
+ *  set_lpf_bma250() - set lpf value
+ */
+
+static int set_lpf_bma250(struct inv_mpu_iio_s *st, int rate)
+{
+	const short hz[] = {1000, 500, 250, 125, 62, 31, 15, 7};
+	const int   d[] = {7, 6, 5, 4, 3, 2, 1, 0};
+	int i, h, data, result;
+	h = (rate >> 1);
+	i = 0;
+	while ((h < hz[i]) && (i < ARRAY_SIZE(hz) - 1))
+		i++;
+	data = d[i];
+
+	result = set_3050_bypass(st, true);
+	if (result)
+		return result;
+	result = bma250_set_bandwidth(st, (unsigned char) data);
+	result |= set_3050_bypass(st, false);
+
+	return result ? (-EINVAL) : 0;
+}
+/**
+ *  set_fs_bma250() - set range value
+ */
+
+static int set_fs_bma250(struct inv_mpu_iio_s *st, int fs)
+{
+	int result;
+	result = set_3050_bypass(st, true);
+	if (result)
+		return result;
+	result = bma250_set_range(st, (unsigned char) fs);
+	result |= set_3050_bypass(st, false);
+
+	return result ? (-EINVAL) : 0;
+}
+
+static struct inv_mpu_slave slave_bma250 = {
+	.suspend = suspend_slave_bma250,
+	.resume  = resume_slave_bma250,
+	.setup   = setup_slave_bma250,
+	.combine_data = combine_data_slave_bma250,
+	.get_mode = get_mode_slave_bma250,
+	.set_lpf = set_lpf_bma250,
+	.set_fs  = set_fs_bma250
+};
+
+int inv_register_mpu3050_slave(struct inv_mpu_iio_s *st)
+{
+	st->mpu_slave = &slave_bma250;
+
+	return 0;
+}
+/**
+ *  @}
+ */
+
diff --git a/drivers/staging/iio/industrialio-buffer.c b/drivers/staging/iio/industrialio-buffer.c
index 386ba76..719ad19 100644
--- a/drivers/staging/iio/industrialio-buffer.c
+++ b/drivers/staging/iio/industrialio-buffer.c
@@ -56,6 +56,8 @@
 {
 	struct iio_dev *indio_dev = filp->private_data;
 	struct iio_buffer *rb = indio_dev->buffer;
+	if (rb->stufftoread)
+		return POLLIN | POLLRDNORM;
 
 	poll_wait(filp, &rb->pollq, wait);
 	if (rb->stufftoread)
@@ -295,7 +297,8 @@
 					channels[i].scan_index;
 		}
 		if (indio_dev->masklength && buffer->scan_mask == NULL) {
-			buffer->scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
+			buffer->scan_mask =
+				kcalloc(BITS_TO_LONGS(indio_dev->masklength),
 						    sizeof(*buffer->scan_mask),
 						    GFP_KERNEL);
 			if (buffer->scan_mask == NULL) {
@@ -308,8 +311,8 @@
 	buffer->scan_el_group.name = iio_scan_elements_group_name;
 
 	buffer->scan_el_group.attrs = kcalloc(attrcount + 1,
-					      sizeof(buffer->scan_el_group.attrs[0]),
-					      GFP_KERNEL);
+				      sizeof(buffer->scan_el_group.attrs[0]),
+				      GFP_KERNEL);
 	if (buffer->scan_el_group.attrs == NULL) {
 		ret = -ENOMEM;
 		goto error_free_scan_mask;
@@ -367,7 +370,7 @@
 	struct iio_dev *indio_dev = dev_get_drvdata(dev);
 	struct iio_buffer *buffer = indio_dev->buffer;
 
-	ret = strict_strtoul(buf, 10, &val);
+	ret = kstrtoul(buf, 10, &val);
 	if (ret)
 		return ret;
 
diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c
index d303bfb..3bdc5aa 100644
--- a/drivers/staging/iio/industrialio-core.c
+++ b/drivers/staging/iio/industrialio-core.c
@@ -68,6 +68,8 @@
 	[IIO_ANGL] = "angl",
 	[IIO_TIMESTAMP] = "timestamp",
 	[IIO_CAPACITANCE] = "capacitance",
+	[IIO_PRESSURE] = "pressure",
+	[IIO_QUATERNION] = "quaternion",
 };
 
 static const char * const iio_modifier_names[] = {
@@ -76,6 +78,7 @@
 	[IIO_MOD_Z] = "z",
 	[IIO_MOD_LIGHT_BOTH] = "both",
 	[IIO_MOD_LIGHT_IR] = "ir",
+	[IIO_MOD_R]  = "r",
 };
 
 /* relies on pairs of these shared then separate */
@@ -690,8 +693,8 @@
 		attrcount++;
 
 	indio_dev->chan_attr_group.attrs = kcalloc(attrcount + 1,
-						   sizeof(indio_dev->chan_attr_group.attrs[0]),
-						   GFP_KERNEL);
+				sizeof(indio_dev->chan_attr_group.attrs[0]),
+				GFP_KERNEL);
 	if (indio_dev->chan_attr_group.attrs == NULL) {
 		ret = -ENOMEM;
 		goto error_clear_attrs;
diff --git a/drivers/staging/iio/kfifo_buf.c b/drivers/staging/iio/kfifo_buf.c
index 9f3bd59..5a70244 100644
--- a/drivers/staging/iio/kfifo_buf.c
+++ b/drivers/staging/iio/kfifo_buf.c
@@ -4,6 +4,7 @@
 #include <linux/device.h>
 #include <linux/workqueue.h>
 #include <linux/kfifo.h>
+#include <linux/sched.h>
 #include <linux/mutex.h>
 
 #include "kfifo_buf.h"
@@ -36,6 +37,7 @@
 	kfifo_free(&buf->kf);
 	ret = __iio_allocate_kfifo(buf, buf->buffer.bytes_per_datum,
 				   buf->buffer.length);
+	r->stufftoread = false;
 error_ret:
 	return ret;
 }
@@ -95,9 +97,16 @@
 {
 	int ret;
 	struct iio_kfifo *kf = iio_to_kfifo(r);
-	ret = kfifo_in(&kf->kf, data, r->bytes_per_datum);
-	if (ret != r->bytes_per_datum)
-		return -EBUSY;
+	if (kfifo_avail(&kf->kf) >= r->bytes_per_datum) {
+		ret = kfifo_in(&kf->kf, data, r->bytes_per_datum);
+		if (ret != r->bytes_per_datum)
+			return -EBUSY;
+	} else {
+		return -ENOMEM;
+	}
+	r->stufftoread = true;
+	wake_up_interruptible(&r->pollq);
+
 	return 0;
 }
 
@@ -107,12 +116,18 @@
 	int ret, copied;
 	struct iio_kfifo *kf = iio_to_kfifo(r);
 
-	if (n < r->bytes_per_datum)
+	if (n < r->bytes_per_datum || r->length == 0)
 		return -EINVAL;
 
 	n = rounddown(n, r->bytes_per_datum);
 	ret = kfifo_to_user(&kf->kf, buf, n, &copied);
 
+	if (kfifo_is_empty(&kf->kf))
+		r->stufftoread = false;
+	/* verify it is still empty to avoid race */
+	if (!kfifo_is_empty(&kf->kf))
+		r->stufftoread = true;
+
 	return copied;
 }
 
diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig
index e7e9159..42c7b9a 100644
--- a/drivers/staging/iio/light/Kconfig
+++ b/drivers/staging/iio/light/Kconfig
@@ -3,6 +3,18 @@
 #
 menu "Light sensors"
 
+config SENSORS_BH1721
+	tristate "Rohm BH1721FVC Ambient Light Sensor"
+	depends on I2C && GPIOLIB
+	default n
+	help
+	  If you say yes here you get support for the ROHM
+	  BH1721FVC light sensor. This driver provides ambient
+	  light intensity in lux, accessible through sysfs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bh1721fvc.
+
 config SENSORS_ISL29018
         tristate "ISL 29018 light and proximity sensor"
         depends on I2C
diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile
index 3011fbf..d4dfcc7 100644
--- a/drivers/staging/iio/light/Makefile
+++ b/drivers/staging/iio/light/Makefile
@@ -2,6 +2,7 @@
 # Makefile for industrial I/O Light sensors
 #
 
+obj-$(CONFIG_SENSORS_BH1721)	+= bh1721fvc.o
 obj-$(CONFIG_SENSORS_TSL2563)	+= tsl2563.o
 obj-$(CONFIG_SENSORS_ISL29018)	+= isl29018.o
 obj-$(CONFIG_TSL2583)	+= tsl2583.o
diff --git a/drivers/staging/iio/light/bh1721fvc.c b/drivers/staging/iio/light/bh1721fvc.c
new file mode 100644
index 0000000..9f65c2c
--- /dev/null
+++ b/drivers/staging/iio/light/bh1721fvc.c
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics. All rights reserved.
+ *
+ * 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
+ */
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/bh1721fvc.h>
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../events.h"
+
+#define BH1721FVC_MODE_DATA(_cmd, _delay, _freq, _name) \
+	{.cmd = (_cmd), .delay = (_delay), .freq = (_freq), .name = _name}
+
+#define SAMPLING_FREQUENCY_DEFAULT 5
+#define POLL_DELAY_DEFAULT (NSEC_PER_SEC / SAMPLING_FREQUENCY_DEFAULT)
+
+enum BH1721FVC_STATE_AND_MODE {
+	STATE_POWER_DOWN,
+	STATE_POWER_ON,
+	STATE_AUTO_MEASURE,
+	STATE_H_MEASURE,
+	STATE_L_MEASURE,
+	STATE_MAX,
+};
+
+struct bh1721fvc_mode_data_params {
+	const u8 cmd;
+	const u8 delay;
+	const u8 freq;
+	const char *name;
+};
+
+static const struct bh1721fvc_mode_data_params
+			bh1721fvc_mode_data[STATE_MAX] = {
+	[STATE_POWER_DOWN]	= BH1721FVC_MODE_DATA(0x00, 0, 0, "invalid"),
+	[STATE_POWER_ON]	= BH1721FVC_MODE_DATA(0x01, 0, 0, "invalid"),
+	[STATE_AUTO_MEASURE]	= BH1721FVC_MODE_DATA(0x10, 136, 7, "auto"),
+	[STATE_H_MEASURE]	= BH1721FVC_MODE_DATA(0x12, 120, 8, "high"),
+	[STATE_L_MEASURE]	= BH1721FVC_MODE_DATA(0x13, 16, 62, "low"),
+};
+
+struct bh1721fvc_data {
+	int reset_pin;
+	struct i2c_client *client;
+	struct iio_dev *indio_dev;
+	struct work_struct work_light;
+	bool is_measuring;
+	bool event_en;
+	u16 lux;
+	struct hrtimer timer;
+	struct mutex lock;
+	struct workqueue_struct *wq;
+	ktime_t light_poll_delay;
+	int light_sampling_frequency;
+	enum BH1721FVC_STATE_AND_MODE state;
+	enum BH1721FVC_STATE_AND_MODE measure_mode;
+};
+
+static int bh1721fvc_light_sensor_reset(int reset_pin)
+{
+	int err;
+
+	err = gpio_direction_output(reset_pin, 0);
+	if (err) {
+		pr_err("Failed to make GPIO go low (%d)\n", err);
+		return err;
+	}
+	udelay(2);
+	err = gpio_direction_output(reset_pin, 1);
+	if (err) {
+		pr_err("Failed to make GPIO go high (%d)\n", err);
+		return err;
+	}
+	return 0;
+}
+
+static int bh1721fvc_get_luxvalue(struct i2c_client *client)
+{
+	int err;
+	u16 value;
+
+	err = i2c_master_recv(client, (u8 *)&value, 2);
+	if (err != 2) {
+		pr_err("Light sensor read failed");
+		return err >= 0 ? -EIO : err;
+	}
+
+	be16_to_cpus(&value);
+	/* Scale by 1/1.2 to convert counts to lux */
+	value = value * 5 / 6;
+	return value;
+}
+
+static int bh1721fvc_set_mode(struct bh1721fvc_data *bh1721fvc,
+				enum BH1721FVC_STATE_AND_MODE measure_mode)
+{
+	int err;
+
+	err = i2c_smbus_write_byte(bh1721fvc->client,
+		bh1721fvc_mode_data[measure_mode].cmd);
+	if (err)
+		goto err_write_mode;
+
+	pr_debug("starting poll timer, delay %ldns\n",
+			bh1721fvc_mode_data[measure_mode].delay *
+			NSEC_PER_MSEC);
+
+	hrtimer_start(&bh1721fvc->timer, ns_to_ktime(bh1721fvc_mode_data[
+		measure_mode].delay * NSEC_PER_MSEC),
+		HRTIMER_MODE_REL);
+
+	bh1721fvc->state = measure_mode;
+
+	return 0;
+
+err_write_mode:
+	pr_err("Error writing mode %s to device",
+		bh1721fvc_mode_data[measure_mode].name);
+	i2c_smbus_write_byte(bh1721fvc->client,
+				bh1721fvc_mode_data[STATE_POWER_DOWN].cmd);
+	return err;
+
+}
+
+static int bh1721fvc_disable(struct bh1721fvc_data *bh1721fvc)
+{
+	int err;
+
+	pr_debug("cancelling poll timer\n");
+	hrtimer_cancel(&bh1721fvc->timer);
+	cancel_work_sync(&bh1721fvc->work_light);
+
+	err = i2c_smbus_write_byte(bh1721fvc->client,
+				 bh1721fvc_mode_data[STATE_POWER_DOWN].cmd);
+	if (unlikely(err != 0)) {
+		pr_err("Failed to write byte (STATE_POWER_DOWN)\n");
+		return err;
+	}
+
+	bh1721fvc->state = STATE_POWER_DOWN;
+
+	return 0;
+}
+
+static void bh1721fvc_work_func_light(struct work_struct *work)
+{
+	u16 lux;
+	int err;
+	struct bh1721fvc_data *bh1721fvc = container_of(work,
+							struct bh1721fvc_data,
+							work_light);
+
+	lux = bh1721fvc_get_luxvalue(bh1721fvc->client);
+	if (lux < 0) {
+		pr_err("read word failed! (errno=%d)\n", lux);
+		return;
+	}
+	bh1721fvc->lux = lux;
+
+	pr_debug("lux %#04x (%u)\n", lux, lux);
+	err = iio_push_event(iio_priv_to_dev(bh1721fvc),
+			IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
+				0,
+				IIO_EV_TYPE_THRESH,
+				IIO_EV_DIR_EITHER),
+			iio_get_time_ns());
+	if (err)
+		pr_err("Could not push IIO_LIGHT event");
+}
+
+static enum hrtimer_restart bh1721fvc_timer_func(struct hrtimer *timer)
+{
+	struct bh1721fvc_data *bh1721fvc = container_of(timer,
+							struct bh1721fvc_data,
+							timer);
+
+	queue_work(bh1721fvc->wq, &bh1721fvc->work_light);
+	hrtimer_forward_now(&bh1721fvc->timer, bh1721fvc->light_poll_delay);
+	return HRTIMER_RESTART;
+}
+
+static ssize_t bh1721fvc_mode_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct bh1721fvc_data *bh1721fvc = iio_priv(dev_get_drvdata(dev));
+
+	return sprintf(buf, "%s\n",
+			bh1721fvc_mode_data[bh1721fvc->measure_mode].name);
+}
+
+static ssize_t bh1721fvc_mode_store(struct device *dev,
+			struct device_attribute *attr, const char *buf,
+			size_t size)
+{
+	u8 new_measure_mode;
+	s64 delay_lowbound;
+	struct bh1721fvc_data *bh1721fvc = iio_priv(dev_get_drvdata(dev));
+	int err;
+
+	if (sysfs_streq(buf, bh1721fvc_mode_data[STATE_AUTO_MEASURE].name)) {
+		new_measure_mode = STATE_AUTO_MEASURE;
+	} else if (sysfs_streq(buf,
+				bh1721fvc_mode_data[STATE_H_MEASURE].name)) {
+		new_measure_mode = STATE_H_MEASURE;
+	} else if (sysfs_streq(buf,
+				bh1721fvc_mode_data[STATE_L_MEASURE].name)) {
+		new_measure_mode = STATE_L_MEASURE;
+	} else {
+		pr_err("invalid value %s\n", buf);
+		return -EINVAL;
+	}
+
+	mutex_lock(&bh1721fvc->lock);
+	if (bh1721fvc->measure_mode != new_measure_mode) {
+		delay_lowbound =
+			bh1721fvc_mode_data[new_measure_mode].delay
+			* NSEC_PER_MSEC;
+		if (ktime_to_ns(bh1721fvc->light_poll_delay) < delay_lowbound) {
+			bh1721fvc->light_poll_delay
+				= ns_to_ktime(delay_lowbound);
+			bh1721fvc->light_sampling_frequency =
+				bh1721fvc_mode_data[new_measure_mode].freq;
+		}
+		if (bh1721fvc->state != STATE_POWER_DOWN) {
+			hrtimer_cancel(&bh1721fvc->timer);
+			cancel_work_sync(&bh1721fvc->work_light);
+			err = bh1721fvc_set_mode(bh1721fvc, new_measure_mode);
+			if (err) {
+				pr_err("Failed to change to mode %s",
+					bh1721fvc_mode_data[new_measure_mode].
+					name);
+				mutex_unlock(&bh1721fvc->lock);
+				return -EIO;
+			}
+		}
+		bh1721fvc->measure_mode = new_measure_mode;
+	}
+	mutex_unlock(&bh1721fvc->lock);
+
+	return size;
+}
+
+static ssize_t bh1721fvc_sampling_frequency_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct bh1721fvc_data *bh1721fvc = iio_priv(dev_get_drvdata(dev));
+
+	pr_debug("delay: %lld delay_hz: %d",
+		ktime_to_ns(bh1721fvc->light_poll_delay),
+		bh1721fvc->light_sampling_frequency);
+	return sprintf(buf, "%d\n", bh1721fvc->light_sampling_frequency);
+}
+
+static ssize_t bh1721fvc_sampling_frequency_store(struct device *dev,
+				struct device_attribute *attr, const char *buf,
+				size_t size)
+{
+	struct bh1721fvc_data *bh1721fvc = iio_priv(dev_get_drvdata(dev));
+
+	int new_freq_hz;
+	s64 new_delay_ns;
+	s64 delay_lowbound;
+	int err;
+
+	err = kstrtoint(buf, 10, &new_freq_hz);
+	if (err < 0)
+		return err;
+
+	if (new_freq_hz <= 0)
+		return -EINVAL;
+
+	/* Conversion from Hz to ns */
+	new_delay_ns = NSEC_PER_SEC / new_freq_hz;
+	delay_lowbound = bh1721fvc_mode_data[bh1721fvc->measure_mode].delay
+				* NSEC_PER_MSEC;
+
+	if (new_delay_ns < delay_lowbound) {
+		new_delay_ns = delay_lowbound;
+		new_freq_hz =
+			bh1721fvc_mode_data[bh1721fvc->measure_mode].freq;
+	}
+	pr_debug("new delay = %lldns, old delay = %lldns\n", new_delay_ns,
+		ktime_to_ns(bh1721fvc->light_poll_delay));
+
+
+	mutex_lock(&bh1721fvc->lock);
+	if (new_delay_ns != ktime_to_ns(bh1721fvc->light_poll_delay)) {
+		bh1721fvc->light_poll_delay = ns_to_ktime(new_delay_ns);
+		bh1721fvc->light_sampling_frequency = new_freq_hz;
+	}
+	mutex_unlock(&bh1721fvc->lock);
+
+	return size;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+	bh1721fvc_mode_show, bh1721fvc_mode_store, 0);
+static IIO_CONST_ATTR(mode_available, "auto high low");
+static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR,
+	bh1721fvc_sampling_frequency_show, bh1721fvc_sampling_frequency_store);
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("auto: <=7Hz, high: <=8Hz, low <=62Hz");
+
+#define BH1721FVC_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
+#define BH1721FVC_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr)
+static struct attribute *bh1721fvc_attributes[] = {
+	BH1721FVC_DEV_ATTR(mode),
+	BH1721FVC_CONST_ATTR(mode_available),
+	BH1721FVC_DEV_ATTR(sampling_frequency),
+	BH1721FVC_CONST_ATTR(sampling_frequency_available),
+	NULL
+};
+
+static const struct attribute_group bh1721fvc_attribute_group = {
+	.attrs = bh1721fvc_attributes,
+};
+
+static const struct iio_chan_spec bh1721fvc_channels[] = {
+	{
+		.type = IIO_LIGHT,
+		.indexed = 1,
+		.channel = 0,
+		.processed_val = IIO_PROCESSED,
+		.event_mask = (IIO_EV_BIT(IIO_EV_TYPE_THRESH,
+				IIO_EV_DIR_EITHER)),
+	}
+};
+
+static int bh1721fvc_read_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int *val, int *val2, long m)
+{
+	struct bh1721fvc_data *bh1721fvc = iio_priv(indio_dev);
+
+	pr_debug("Reporting lux(%d) to user", bh1721fvc->lux);
+	*val = bh1721fvc->lux;
+
+	return IIO_VAL_INT;
+}
+
+static int bh1721fvc_read_event_value(struct iio_dev *indio_dev,
+			       u64 event_code,
+			       int *val)
+{
+	struct bh1721fvc_data *bh1721fvc = iio_priv(indio_dev);
+
+	*val = bh1721fvc->lux;
+
+	return 0;
+}
+
+static int bh1721fvc_read_event_config(struct iio_dev *indio_dev,
+				       u64 event_code)
+{
+	struct bh1721fvc_data *bh1721fvc = iio_priv(indio_dev);
+
+	pr_debug("is_measuring = %d", bh1721fvc->state != STATE_POWER_DOWN);
+	return (bh1721fvc->state != STATE_POWER_DOWN);
+}
+
+static int bh1721fvc_write_event_config(struct iio_dev *indio_dev,
+				       u64 event_code,
+				       int state)
+{
+	int err = 0;
+	struct bh1721fvc_data *bh1721fvc = iio_priv(indio_dev);
+
+	pr_debug("state %d->%d\n", (bh1721fvc->state != STATE_POWER_DOWN),
+			state);
+
+	mutex_lock(&bh1721fvc->lock);
+	if (state && (bh1721fvc->state == STATE_POWER_DOWN)) {
+		err = bh1721fvc_set_mode(bh1721fvc, bh1721fvc->measure_mode);
+		if (err)
+			goto err_set_mode;
+	} else if (!state && (bh1721fvc->state != STATE_POWER_DOWN)) {
+		err = bh1721fvc_disable(bh1721fvc);
+		if (err)
+			goto err_set_mode;
+	}
+	bh1721fvc->is_measuring = state;
+	mutex_unlock(&bh1721fvc->lock);
+
+	return 0;
+
+err_set_mode:
+	mutex_unlock(&bh1721fvc->lock);
+	return err;
+}
+
+static const struct iio_info bh1721fvc_info = {
+	.attrs = &bh1721fvc_attribute_group,
+	.driver_module = THIS_MODULE,
+	.read_raw = bh1721fvc_read_raw,
+	.read_event_value = bh1721fvc_read_event_value,
+	.read_event_config = bh1721fvc_read_event_config,
+	.write_event_config = bh1721fvc_write_event_config,
+};
+
+static int __devinit bh1721fvc_i2c_probe(struct i2c_client *client,
+					 const struct i2c_device_id *id)
+{
+	int err;
+	struct bh1721fvc_data *bh1721fvc;
+	struct iio_dev *indio_dev;
+	struct bh1721fvc_platform_data *pdata;
+	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_I2C |
+					I2C_FUNC_SMBUS_WRITE_BYTE))
+		return -ENOSYS;
+
+	pdata = client->dev.platform_data;
+	if (!pdata) {
+		pr_err("no platform data\n");
+		return -EINVAL;
+	}
+
+	indio_dev = iio_allocate_device(sizeof(*bh1721fvc));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	bh1721fvc = iio_priv(indio_dev);
+	i2c_set_clientdata(client, indio_dev);
+	bh1721fvc->client = client;
+
+	bh1721fvc->reset_pin = pdata->reset_pin;
+	if (bh1721fvc->reset_pin < 0) {
+		pr_err("reset pin is invalid\n");
+		err = -EINVAL;
+		goto err_reset_invalid;
+	}
+
+	err = gpio_request_one(bh1721fvc->reset_pin, GPIOF_OUT_INIT_HIGH,
+			"ALS_NRST");
+	if (err) {
+		pr_err("Failed to request ALS_NRST for light sensor reset\n");
+		goto err_reset_request;
+	}
+
+	err = bh1721fvc_light_sensor_reset(bh1721fvc->reset_pin);
+	if (err) {
+		pr_err("Failed to reset\n");
+		goto err_reset_failed;
+	}
+
+	mutex_init(&bh1721fvc->lock);
+	hrtimer_init(&bh1721fvc->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+
+	bh1721fvc->light_poll_delay = ns_to_ktime(POLL_DELAY_DEFAULT);
+	bh1721fvc->light_sampling_frequency = SAMPLING_FREQUENCY_DEFAULT;
+	bh1721fvc->state = STATE_POWER_DOWN;
+	bh1721fvc->measure_mode = STATE_AUTO_MEASURE;
+	bh1721fvc->is_measuring = false;
+	bh1721fvc->timer.function = bh1721fvc_timer_func;
+
+	bh1721fvc->wq = alloc_workqueue("bh1721fvc_wq",
+					WQ_UNBOUND | WQ_RESCUER, 1);
+	if (!bh1721fvc->wq) {
+		err = -ENOMEM;
+		pr_err("could not create workqueue\n");
+		goto err_create_workqueue;
+	}
+
+	INIT_WORK(&bh1721fvc->work_light, bh1721fvc_work_func_light);
+
+	indio_dev->name = "lightsensor-level";
+	indio_dev->channels = bh1721fvc_channels;
+	indio_dev->num_channels = ARRAY_SIZE(bh1721fvc_channels);
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &bh1721fvc_info;
+
+	pr_info("registering lightsensor-level input device\n");
+	err = iio_device_register(indio_dev);
+	if (err)
+		goto err_iio_register_device_light;
+	return 0;
+
+err_iio_register_device_light:
+	destroy_workqueue(bh1721fvc->wq);
+err_create_workqueue:
+	mutex_destroy(&bh1721fvc->lock);
+err_reset_failed:
+	gpio_free(bh1721fvc->reset_pin);
+err_reset_request:
+err_reset_invalid:
+	iio_free_device(indio_dev);
+	return err;
+}
+
+static int __devexit bh1721fvc_i2c_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct bh1721fvc_data *bh1721fvc = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+
+	if (bh1721fvc->is_measuring)
+		bh1721fvc_disable(bh1721fvc);
+
+	destroy_workqueue(bh1721fvc->wq);
+	mutex_destroy(&bh1721fvc->lock);
+	gpio_free(bh1721fvc->reset_pin);
+	iio_free_device(indio_dev);
+
+	return 0;
+}
+
+static int bh1721fvc_suspend(struct device *dev)
+{
+	int err = 0;
+	struct bh1721fvc_data *bh1721fvc = iio_priv(i2c_get_clientdata(
+							to_i2c_client(dev)));
+
+	if (bh1721fvc->is_measuring) {
+		err = bh1721fvc_disable(bh1721fvc);
+		if (err)
+			pr_err("could not disable\n");
+	}
+
+	return err;
+}
+
+static int bh1721fvc_resume(struct device *dev)
+{
+	int err;
+	struct bh1721fvc_data *bh1721fvc = iio_priv(i2c_get_clientdata(
+							to_i2c_client(dev)));
+
+	if (bh1721fvc->is_measuring) {
+		err = bh1721fvc_set_mode(bh1721fvc, bh1721fvc->measure_mode);
+		if (err) {
+			pr_err("could not enable\n");
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static const struct i2c_device_id bh1721fvc_device_id[] = {
+	{"bh1721fvc", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, bh1721fvc_device_id);
+
+static const struct dev_pm_ops bh1721fvc_pm_ops = {
+	.suspend = bh1721fvc_suspend,
+	.resume = bh1721fvc_resume,
+};
+
+static struct i2c_driver bh1721fvc_driver = {
+	.driver = {
+		   .name = "bh1721fvc",
+		   .owner = THIS_MODULE,
+		   .pm = &bh1721fvc_pm_ops,
+	},
+	.probe = bh1721fvc_i2c_probe,
+	.remove = __devexit_p(bh1721fvc_i2c_remove),
+	.id_table = bh1721fvc_device_id,
+};
+
+module_i2c_driver(bh1721fvc_driver);
+
+MODULE_AUTHOR("Veeren Mandalia <v.mandalia@sta.samsung.com>");
+MODULE_DESCRIPTION("BH1721FVC Ambient light sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/pressure/Kconfig b/drivers/staging/iio/pressure/Kconfig
new file mode 100644
index 0000000..70f98f9
--- /dev/null
+++ b/drivers/staging/iio/pressure/Kconfig
@@ -0,0 +1,17 @@
+#
+# Pressure sensors
+#
+menu "Pressure sensors"
+
+config BMP182
+	tristate "BMP182 digital pressure sensor"
+	depends on I2C
+	default n
+	help
+	  If you say yes here you get support for the Bosch Sensortec
+	  BMP182 digital pressure sensor.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bmp182.
+
+endmenu
diff --git a/drivers/staging/iio/pressure/Makefile b/drivers/staging/iio/pressure/Makefile
new file mode 100644
index 0000000..2fe005f
--- /dev/null
+++ b/drivers/staging/iio/pressure/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for industrial I/O Pressure sensors
+#
+
+obj-$(CONFIG_BMP182)		+= bmp182.o
diff --git a/drivers/staging/iio/pressure/bmp182.c b/drivers/staging/iio/pressure/bmp182.c
new file mode 100644
index 0000000..2864c6c
--- /dev/null
+++ b/drivers/staging/iio/pressure/bmp182.c
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics. All rights reserved.
+ *
+ * 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
+ */
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include "../iio.h"
+#include "../events.h"
+#include "../sysfs.h"
+
+#define DRIVER_VERSION		"1.0"
+
+/* Register definitions */
+#define BMP182_TAKE_MEAS_REG		0xf4
+#define BMP182_READ_MEAS_REG_U		0xf6
+#define BMP182_READ_MEAS_REG_L		0xf7
+#define BMP182_READ_MEAS_REG_XL		0xf8
+
+/*
+ * Bytes defined by the spec to take measurements
+ * Temperature will take 4.5ms before EOC
+ */
+#define BMP182_MEAS_TEMP		0x2e
+/* 4.5ms wait for measurement */
+#define BMP182_MEAS_PRESS_OVERSAMP_0	0x34
+/* 7.5ms wait for measurement */
+#define BMP182_MEAS_PRESS_OVERSAMP_1	0x74
+/* 13.5ms wait for measurement */
+#define BMP182_MEAS_PRESS_OVERSAMP_2	0xb4
+/* 25.5ms wait for measurement */
+#define BMP182_MEAS_PRESS_OVERSAMP_3	0xf4
+
+/*
+ * EEPROM registers each is a two byte value so there is
+ * an upper byte and a lower byte
+ */
+#define BMP182_EEPROM_AC1_U	0xaa
+#define BMP182_EEPROM_AC1_L	0xab
+#define BMP182_EEPROM_AC2_U	0xac
+#define BMP182_EEPROM_AC2_L	0xad
+#define BMP182_EEPROM_AC3_U	0xae
+#define BMP182_EEPROM_AC3_L	0xaf
+#define BMP182_EEPROM_AC4_U	0xb0
+#define BMP182_EEPROM_AC4_L	0xb1
+#define BMP182_EEPROM_AC5_U	0xb2
+#define BMP182_EEPROM_AC5_L	0xb3
+#define BMP182_EEPROM_AC6_U	0xb4
+#define BMP182_EEPROM_AC6_L	0xb5
+#define BMP182_EEPROM_B1_U	0xb6
+#define BMP182_EEPROM_B1_L	0xb7
+#define BMP182_EEPROM_B2_U	0xb8
+#define BMP182_EEPROM_B2_L	0xb9
+#define BMP182_EEPROM_MB_U	0xba
+#define BMP182_EEPROM_MB_L	0xbb
+#define BMP182_EEPROM_MC_U	0xbc
+#define BMP182_EEPROM_MC_L	0xbd
+#define BMP182_EEPROM_MD_U	0xbe
+#define BMP182_EEPROM_MD_L	0xbf
+
+#define SAMP_FREQ_MAX		20
+#define SAMP_FREQ_MIN		1
+#define SAMP_FREQ_DEFAULT	5
+
+#define PRESSURE_MAX		125000
+#define PRESSURE_MIN		95000
+#define PRESSURE_FUZZ		5
+#define PRESSURE_FLAT		5
+
+struct bmp182_eeprom_data {
+	s16 AC1, AC2, AC3;
+	u16 AC4, AC5, AC6;
+	s16 B1, B2;
+	s16 MB, MC, MD;
+} __aligned(2) __packed;
+
+struct bmp182_data {
+	struct i2c_client *client;
+	struct mutex lock;
+	struct workqueue_struct *wq;
+	struct work_struct work_pressure;
+	bool on_before_suspend;
+	bool enabled;
+	u8 oversampling_rate;
+	int pressure;
+	struct hrtimer timer;
+	int sampling_freq;
+	ktime_t poll_delay;
+	struct bmp182_eeprom_data bmp182_eeprom_vals;
+};
+
+static void bmp182_enable(struct bmp182_data *barom)
+{
+	if (!barom->enabled) {
+		barom->enabled = true;
+		pr_debug("start timer\n");
+		hrtimer_start(&barom->timer, barom->poll_delay,
+			HRTIMER_MODE_REL);
+	}
+}
+
+static void bmp182_disable(struct bmp182_data *barom)
+{
+	if (barom->enabled) {
+		barom->enabled = false;
+		pr_debug("stop timer\n");
+		hrtimer_cancel(&barom->timer);
+		cancel_work_sync(&barom->work_pressure);
+	}
+}
+
+static int bmp182_get_raw_temperature(struct bmp182_data *barom,
+					u16 *raw_temperature)
+{
+	int err;
+	u16 buf;
+
+	err = i2c_smbus_write_byte_data(barom->client,
+				BMP182_TAKE_MEAS_REG,
+				BMP182_MEAS_TEMP);
+	if (err) {
+		pr_err("can't write BMP182_TAKE_MEAS_REG\n");
+		return err;
+	}
+
+	usleep_range(5000, 6000);
+
+	err = i2c_smbus_read_i2c_block_data(barom->client,
+				BMP182_READ_MEAS_REG_U,
+				sizeof(buf), (u8 *)&buf);
+	if (err != sizeof(buf)) {
+		pr_err("Fail to read uncompensated temperature\n");
+		return err >= 0 ? -EIO : err;
+	}
+	*raw_temperature = be16_to_cpu(buf);
+	pr_debug("uncompensated temperature:  %d\n", *raw_temperature);
+	return 0;
+}
+
+static int bmp182_get_raw_pressure(struct bmp182_data *barom,
+					u32 *raw_pressure)
+{
+	int err;
+	u32 buf = 0;
+	int range;
+
+	err = i2c_smbus_write_byte_data(barom->client,
+				BMP182_TAKE_MEAS_REG,
+				BMP182_MEAS_PRESS_OVERSAMP_0 |
+				(barom->oversampling_rate << 6));
+	if (err) {
+		pr_err("can't write BMP182_TAKE_MEAS_REG\n");
+		return err;
+	}
+
+	range = 2 + (3 << barom->oversampling_rate);
+	usleep_range(range * 1000, (range + 1) * 1000);
+
+	err = i2c_smbus_read_i2c_block_data(barom->client,
+			BMP182_READ_MEAS_REG_U, 3, ((u8 *)&buf) + 1);
+	if (err != 3) {
+		pr_err("Fail to read uncompensated pressure\n");
+		return err >= 0 ? -EIO : err;
+	}
+
+	*raw_pressure = be32_to_cpu(buf);
+	*raw_pressure >>= (8 - barom->oversampling_rate);
+	pr_debug("uncompensated pressure:  %d\n", *raw_pressure);
+	return 0;
+}
+
+static void bmp182_get_pressure_data(struct work_struct *work)
+{
+	u16 raw_temperature;
+	u32 raw_pressure;
+	s32 x1, x2, x3, b3, b5, b6;
+	u32 b4;
+	s32 p;
+
+	struct bmp182_data *barom =
+	    container_of(work, struct bmp182_data, work_pressure);
+
+	if (bmp182_get_raw_temperature(barom, &raw_temperature)) {
+		pr_err("can't read uncompensated temperature\n");
+		return;
+	}
+
+	if (bmp182_get_raw_pressure(barom, &raw_pressure)) {
+		pr_err("Fail to read uncompensated pressure\n");
+		return;
+	}
+
+	/* voodoo from BMP182 data sheet, BST-BMP182-DS000-00, page 15 */
+	x1 = ((raw_temperature - barom->bmp182_eeprom_vals.AC6) *
+	      barom->bmp182_eeprom_vals.AC5) >> 15;
+	x2 = (barom->bmp182_eeprom_vals.MC << 11) /
+	    (x1 + barom->bmp182_eeprom_vals.MD);
+
+	b5 = (x1 + x2 - 4000);
+	x1 = (barom->bmp182_eeprom_vals.B2 * ((b5 * b5) >> 12));
+	x2 = (barom->bmp182_eeprom_vals.AC2 * b5);
+	x3 = (x1 + x2) >> 11;
+	b3 = (((((s32)barom->bmp182_eeprom_vals.AC1) * 4 +
+		x3) << barom->oversampling_rate) + 2) >> 2;
+	x1 = (barom->bmp182_eeprom_vals.AC3 * b5) >> 13;
+	x2 = (barom->bmp182_eeprom_vals.B1 * (b5 * b5 >> 12)) >> 16;
+	x3 = ((x1 + x2) + 2) >> 2;
+	b4 = (barom->bmp182_eeprom_vals.AC4 *
+	      (u32)(x3 + 32768)) >> 15;
+	b6 = (raw_pressure - b3) *
+		(50000 >> barom->oversampling_rate);
+	if (b6 < 0x80000000)
+		p = (b6 * 2) / b4;
+	else
+		p = (b6 / b4) * 2;
+
+	x1 = (p >> 8) * (p >> 8);
+	x1 = (x1 * 3038) >> 16;
+	x2 = (-7357 * p) >> 16;
+
+	barom->pressure = p + ((x1 + x2 + 3791) >> 4);
+	pr_debug("calibrated pressure: %d\n", barom->pressure);
+
+	if (iio_push_event(iio_priv_to_dev(barom),
+			IIO_UNMOD_EVENT_CODE(IIO_PRESSURE,
+				0,
+				IIO_EV_TYPE_THRESH,
+				IIO_EV_DIR_EITHER),
+			iio_get_time_ns()))
+		pr_err("Could not push IIO_PRESSURE event");
+}
+
+static int __devinit bmp182_read_store_eeprom_val(struct bmp182_data *barom)
+{
+	int err;
+
+	err = i2c_smbus_read_i2c_block_data(barom->client,
+				BMP182_EEPROM_AC1_U,
+				sizeof(barom->bmp182_eeprom_vals),
+				(u8 *)&(barom->bmp182_eeprom_vals));
+	if (err != sizeof(barom->bmp182_eeprom_vals)) {
+		pr_err("Cannot read EEPROM values\n");
+		return err >= 0 ? -EIO : err;
+	}
+	be16_to_cpus((u16 *)&(barom->bmp182_eeprom_vals.AC1));
+	be16_to_cpus((u16 *)&(barom->bmp182_eeprom_vals.AC2));
+	be16_to_cpus((u16 *)&(barom->bmp182_eeprom_vals.AC3));
+	be16_to_cpus(&(barom->bmp182_eeprom_vals.AC4));
+	be16_to_cpus(&(barom->bmp182_eeprom_vals.AC5));
+	be16_to_cpus(&(barom->bmp182_eeprom_vals.AC6));
+	be16_to_cpus((u16 *)&(barom->bmp182_eeprom_vals.B1));
+	be16_to_cpus((u16 *)&(barom->bmp182_eeprom_vals.B2));
+	be16_to_cpus((u16 *)&(barom->bmp182_eeprom_vals.MB));
+	be16_to_cpus((u16 *)&(barom->bmp182_eeprom_vals.MC));
+	be16_to_cpus((u16 *)&(barom->bmp182_eeprom_vals.MD));
+	return 0;
+}
+
+static enum hrtimer_restart bmp182_timer_func(struct hrtimer *timer)
+{
+	struct bmp182_data *barom = container_of(timer,
+		struct bmp182_data, timer);
+
+	pr_debug("start\n");
+	queue_work(barom->wq, &barom->work_pressure);
+	hrtimer_forward_now(&barom->timer, barom->poll_delay);
+	return HRTIMER_RESTART;
+}
+
+static ssize_t bmp182_sampling_frequency_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct bmp182_data *barom = iio_priv(dev_get_drvdata(dev));
+
+	return sprintf(buf, "%d Hz\n", barom->sampling_freq);
+}
+
+static ssize_t bmp182_sampling_frequency_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf,
+				size_t size)
+{
+	unsigned int new_value;
+	struct bmp182_data *barom = iio_priv(dev_get_drvdata(dev));
+	int err;
+
+	err = kstrtouint(buf, 10, &new_value);
+	if (err)
+		return err;
+
+	if (new_value < SAMP_FREQ_MIN)
+		new_value = SAMP_FREQ_MIN;
+	else if (new_value > SAMP_FREQ_MAX)
+		new_value = SAMP_FREQ_MAX;
+
+	pr_debug("new frequency = %dHz\n", new_value);
+
+	mutex_lock(&barom->lock);
+	if (new_value != barom->sampling_freq) {
+		barom->sampling_freq = new_value;
+		barom->poll_delay = ns_to_ktime(NSEC_PER_SEC / new_value);
+	}
+	mutex_unlock(&barom->lock);
+
+	return size;
+}
+
+static ssize_t bmp182_oversampling_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct bmp182_data *barom = iio_priv(dev_get_drvdata(dev));
+
+	return sprintf(buf, "%d\n", barom->oversampling_rate);
+}
+
+static ssize_t bmp182_oversampling_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf,
+				size_t count)
+{
+	struct bmp182_data *barom = iio_priv(dev_get_drvdata(dev));
+	unsigned long oversampling;
+	int err = kstrtoul(buf, 10, &oversampling);
+
+	if (err)
+		return err;
+	if (oversampling > 3)
+		oversampling = 3;
+	barom->oversampling_rate = oversampling;
+	return count;
+}
+
+static int bmp182_read_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *chan,
+				int *val,
+				int *val2,
+				long mask)
+{
+	struct bmp182_data *barom = iio_priv(indio_dev);
+
+	*val = barom->pressure;
+	return IIO_VAL_INT;
+}
+
+static int bmp182_read_event_config(struct iio_dev *indio_dev,
+						u64 event_code)
+{
+	struct bmp182_data *barom = iio_priv(indio_dev);
+
+	return barom->enabled;
+}
+
+static int bmp182_write_event_config(struct iio_dev *indio_dev,
+						u64 event_code,
+						int state)
+{
+	struct bmp182_data *barom = iio_priv(indio_dev);
+
+	pr_debug("enable = %d, old state = %d\n",
+		state, barom->enabled);
+
+	mutex_lock(&barom->lock);
+	if (state)
+		bmp182_enable(barom);
+	else
+		bmp182_disable(barom);
+	mutex_unlock(&barom->lock);
+
+	return 0;
+}
+
+static int bmp182_read_event_value(struct iio_dev *indio_dev,
+						u64 event_code,
+						int *val)
+{
+	struct bmp182_data *barom = iio_priv(indio_dev);
+
+	*val = barom->pressure;
+	return 0;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR,
+		bmp182_sampling_frequency_show,
+		bmp182_sampling_frequency_store);
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1 ~ 20Hz");
+static IIO_DEVICE_ATTR(oversampling, S_IRUGO | S_IWUSR,
+		bmp182_oversampling_show,
+		bmp182_oversampling_store,
+		0);
+static IIO_CONST_ATTR(oversampling_modes, "0 1 2 3");
+
+#define BMP182_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
+#define BMP182_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr)
+static struct attribute *bmp182_sysfs_attrs[] = {
+	BMP182_DEV_ATTR(sampling_frequency),
+	BMP182_CONST_ATTR(sampling_frequency_available),
+	BMP182_DEV_ATTR(oversampling),
+	BMP182_CONST_ATTR(oversampling_modes),
+	NULL
+};
+
+static struct attribute_group bmp182_attribute_group = {
+	.attrs = bmp182_sysfs_attrs,
+};
+
+static const struct iio_info bmp182_info = {
+	.attrs = &bmp182_attribute_group,
+	.read_raw = bmp182_read_raw,
+	.read_event_config = bmp182_read_event_config,
+	.write_event_config = bmp182_write_event_config,
+	.read_event_value = bmp182_read_event_value,
+	.driver_module = THIS_MODULE,
+};
+
+static const struct iio_chan_spec bmp182_channels[] = {
+	{
+		.type = IIO_PRESSURE,
+		.indexed = 1,
+		.channel = 0,
+		.processed_val = IIO_PROCESSED,
+		.event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH,
+					IIO_EV_DIR_EITHER),
+	}
+};
+
+static int __devinit bmp182_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	int err;
+	struct bmp182_data *barom;
+	struct iio_dev *indio_dev;
+
+	pr_debug("enter\n");
+	if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_SMBUS_WRITE_BYTE |
+				I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+		pr_err("client not i2c capable\n");
+		return -ENOSYS;
+	}
+
+	indio_dev = iio_allocate_device(sizeof(*barom));
+	if (!indio_dev) {
+		pr_err("failed to allocate memory for iio device\n");
+		return -ENOMEM;
+	}
+
+	barom = iio_priv(indio_dev);
+	mutex_init(&barom->lock);
+	barom->client = client;
+
+	i2c_set_clientdata(client, indio_dev);
+
+	err = bmp182_read_store_eeprom_val(barom);
+	if (err) {
+		pr_err("Reading the EEPROM failed\n");
+		err = -ENODEV;
+		goto err_read_eeprom;
+	}
+
+	hrtimer_init(&barom->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	barom->sampling_freq = SAMP_FREQ_DEFAULT;
+	barom->poll_delay = ns_to_ktime(NSEC_PER_SEC / SAMP_FREQ_DEFAULT);
+	barom->timer.function = bmp182_timer_func;
+
+	barom->wq = alloc_workqueue("bmp182_wq",
+		WQ_UNBOUND | WQ_RESCUER, 1);
+	if (!barom->wq) {
+		err = -ENOMEM;
+		pr_err("could not create workqueue\n");
+		goto err_create_workqueue;
+	}
+
+	INIT_WORK(&barom->work_pressure, bmp182_get_pressure_data);
+
+	indio_dev->name = "barometer";
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->info = &bmp182_info;
+	indio_dev->channels = bmp182_channels;
+	indio_dev->num_channels = ARRAY_SIZE(bmp182_channels);
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	err = iio_device_register(indio_dev);
+	if (err)
+		goto err_iio_register;
+
+	pr_debug("%s sensor registered\n", id->name);
+	return 0;
+
+err_iio_register:
+	destroy_workqueue(barom->wq);
+err_create_workqueue:
+err_read_eeprom:
+	mutex_destroy(&barom->lock);
+	iio_free_device(indio_dev);
+	return err;
+}
+
+static int __devexit bmp182_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct bmp182_data *barom = iio_priv(indio_dev);
+
+	bmp182_disable(barom);
+	destroy_workqueue(barom->wq);
+	mutex_destroy(&barom->lock);
+	iio_device_unregister(indio_dev);
+	iio_free_device(indio_dev);
+	return 0;
+}
+
+static int bmp182_resume(struct device *dev)
+{
+	struct bmp182_data *barom = iio_priv(i2c_get_clientdata(
+					to_i2c_client(dev)));
+
+	pr_debug("on_before_suspend %d\n", barom->on_before_suspend);
+
+	if (barom->on_before_suspend)
+		bmp182_enable(barom);
+	return 0;
+}
+
+static int bmp182_suspend(struct device *dev)
+{
+	struct bmp182_data *barom = iio_priv(i2c_get_clientdata(
+					to_i2c_client(dev)));
+
+	barom->on_before_suspend = barom->enabled;
+	pr_debug("on_before_suspend %d\n", barom->on_before_suspend);
+	bmp182_disable(barom);
+	return 0;
+}
+
+static const struct i2c_device_id bmp182_id[] = {
+	{"bmp182", 0},
+	{},
+};
+
+MODULE_DEVICE_TABLE(i2c, bmp182_id);
+static const struct dev_pm_ops bmp182_pm_ops = {
+	.suspend	= bmp182_suspend,
+	.resume		= bmp182_resume,
+};
+
+static struct i2c_driver bmp182_driver = {
+	.driver = {
+		.name	= "bmp182",
+		.owner	= THIS_MODULE,
+		.pm	= &bmp182_pm_ops,
+	},
+	.probe		= bmp182_probe,
+	.remove		= __devexit_p(bmp182_remove),
+	.id_table	= bmp182_id,
+};
+
+module_i2c_driver(bmp182_driver);
+
+MODULE_AUTHOR("Hyoung Wook Ham <hwham@sta.samsung.com>");
+MODULE_DESCRIPTION("BMP182 Pressure sensor driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/staging/iio/types.h b/drivers/staging/iio/types.h
index 0c32136..7873357 100644
--- a/drivers/staging/iio/types.h
+++ b/drivers/staging/iio/types.h
@@ -27,6 +27,8 @@
 	IIO_ANGL,
 	IIO_TIMESTAMP,
 	IIO_CAPACITANCE,
+	IIO_PRESSURE,
+	IIO_QUATERNION,
 };
 
 enum iio_modifier {
@@ -44,6 +46,7 @@
 	IIO_MOD_X_OR_Y_OR_Z,
 	IIO_MOD_LIGHT_BOTH,
 	IIO_MOD_LIGHT_IR,
+	IIO_MOD_R,
 };
 
 #define IIO_VAL_INT 1
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index 18e3a14..5ce1b055 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -875,6 +875,13 @@
 	return 0;
 }
 
+static void s3c24xx_serial_wake_peer(struct uart_port *port)
+{
+	struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
+
+	if (cfg->wake_peer)
+		cfg->wake_peer(port);
+}
 
 #ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
 
@@ -903,6 +910,7 @@
 	.request_port	= s3c24xx_serial_request_port,
 	.config_port	= s3c24xx_serial_config_port,
 	.verify_port	= s3c24xx_serial_verify_port,
+	.wake_peer	= s3c24xx_serial_wake_peer,
 };
 
 static struct uart_driver s3c24xx_uart_drv = {
diff --git a/drivers/usb/gadget/s3c_udc.h b/drivers/usb/gadget/s3c_udc.h
index 2c21ef8..bfb36ac 100644
--- a/drivers/usb/gadget/s3c_udc.h
+++ b/drivers/usb/gadget/s3c_udc.h
@@ -140,6 +140,9 @@
 	struct resource *regs_res;
 	unsigned int irq;
 	unsigned req_pending:1, req_std:1, req_config:1;
+	int udc_enabled:1;
+	int soft_connected:1;
+	struct usb_phy *phy;
 };
 
 extern struct s3c_udc *the_controller;
diff --git a/drivers/usb/gadget/s3c_udc_otg.c b/drivers/usb/gadget/s3c_udc_otg.c
index ad85378..17a001f 100644
--- a/drivers/usb/gadget/s3c_udc_otg.c
+++ b/drivers/usb/gadget/s3c_udc_otg.c
@@ -24,6 +24,7 @@
 #include <linux/clk.h>
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
+#include <linux/usb/otg.h>
 #include <plat/regs-otg.h>
 #include <plat/usb-phy.h>
 #include <plat/udc-hs.h>
@@ -141,8 +142,7 @@
 static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed);
 static void nuke(struct s3c_ep *ep, int status);
 static int s3c_udc_set_halt(struct usb_ep *_ep, int value);
-static void s3c_udc_soft_connect(void);
-static void s3c_udc_soft_disconnect(void);
+static void s3c_udc_update_soft_flag(void);
 
 static struct usb_ep_ops s3c_ep_ops = {
 	.enable = s3c_ep_enable,
@@ -216,7 +216,6 @@
 {
 	struct platform_device *pdev = dev->dev;
 	struct s3c_hsotg_plat *pdata = pdev->dev.platform_data;
-	u32 utemp;
 	DEBUG_SETUP("%s: %p\n", __func__, dev);
 
 	disable_irq(dev->irq);
@@ -229,12 +228,9 @@
 	/* Mask the core interrupt */
 	__raw_writel(0, dev->regs + S3C_UDC_OTG_GINTMSK);
 
-	/* Put the OTG device core in the disconnected state.*/
-	utemp = __raw_readl(dev->regs + S3C_UDC_OTG_DCTL);
-	utemp |= SOFT_DISCONNECT;
-	__raw_writel(utemp, dev->regs + S3C_UDC_OTG_DCTL);
-	udelay(20);
-	if (pdata && pdata->phy_exit)
+	if (dev->phy)
+		usb_phy_shutdown(dev->phy);
+	else if (pdata && pdata->phy_exit)
 		pdata->phy_exit(pdev, S5P_USB_PHY_DEVICE);
 	clk_disable(dev->clk);
 }
@@ -283,7 +279,9 @@
 
 	enable_irq(dev->irq);
 	clk_enable(dev->clk);
-	if (pdata->phy_init)
+	if (dev->phy)
+		usb_phy_init(dev->phy);
+	else if (pdata->phy_init)
 		pdata->phy_init(pdev, S5P_USB_PHY_DEVICE);
 	reconfig_usbd();
 
@@ -300,16 +298,26 @@
 	unsigned long flags;
 	struct s3c_udc *dev = container_of(gadget, struct s3c_udc, gadget);
 
-	if (!is_active) {
-		spin_lock_irqsave(&dev->lock, flags);
-		stop_activity(dev, dev->driver);
-		spin_unlock_irqrestore(&dev->lock, flags);
-		udc_disable(dev);
-	} else {
-		udc_reinit(dev);
-		udc_enable(dev);
-		s3c_udc_soft_connect();
+	spin_lock_irqsave(&dev->lock, flags);
+	if (dev->udc_enabled != is_active) {
+		dev->udc_enabled = is_active;
+
+		if (!is_active) {
+			s3c_udc_update_soft_flag();
+			spin_unlock_irqrestore(&dev->lock, flags);
+			udc_disable(dev);
+			spin_lock_irqsave(&dev->lock, flags);
+		} else {
+			udc_reinit(dev);
+			spin_unlock_irqrestore(&dev->lock, flags);
+
+			udc_enable(dev);
+
+			spin_lock_irqsave(&dev->lock, flags);
+			s3c_udc_update_soft_flag();
+		}
 	}
+	spin_unlock_irqrestore(&dev->lock, flags);
 
 	return 0;
 }
@@ -349,8 +357,6 @@
 
 	printk(KERN_INFO "bound driver '%s'\n",
 			driver->driver.name);
-	udc_enable(dev);
-
 	return 0;
 }
 
@@ -824,39 +830,35 @@
 	return -ENOTSUPP;
 }
 
-static void s3c_udc_soft_connect(void)
+static void s3c_udc_update_soft_flag(void)
 {
 	struct s3c_udc *dev = the_controller;
-	u32 uTemp;
+	u32 val;
 
-	DEBUG("[%s]\n", __func__);
-	uTemp = __raw_readl(dev->regs + S3C_UDC_OTG_DCTL);
-	uTemp = uTemp & ~SOFT_DISCONNECT;
-	__raw_writel(uTemp, dev->regs + S3C_UDC_OTG_DCTL);
-}
-
-static void s3c_udc_soft_disconnect(void)
-{
-	struct s3c_udc *dev = the_controller;
-	u32 uTemp;
-	unsigned long flags;
-
-	DEBUG("[%s]\n", __func__);
-	uTemp = __raw_readl(dev->regs + S3C_UDC_OTG_DCTL);
-	uTemp |= SOFT_DISCONNECT;
-	__raw_writel(uTemp, dev->regs + S3C_UDC_OTG_DCTL);
-
-	spin_lock_irqsave(&dev->lock, flags);
-	stop_activity(dev, dev->driver);
-	spin_unlock_irqrestore(&dev->lock, flags);
+	if (dev->udc_enabled && dev->soft_connected) {
+		val = __raw_readl(dev->regs + S3C_UDC_OTG_DCTL);
+		val &= ~SOFT_DISCONNECT;
+		__raw_writel(val, dev->regs + S3C_UDC_OTG_DCTL);
+	} else {
+		val = __raw_readl(dev->regs + S3C_UDC_OTG_DCTL);
+		if (!(val & SOFT_DISCONNECT)) {
+			val |= SOFT_DISCONNECT;
+			__raw_writel(val, dev->regs + S3C_UDC_OTG_DCTL);
+			stop_activity(dev, dev->driver);
+		}
+	}
 }
 
 static int s3c_udc_pullup(struct usb_gadget *gadget, int is_on)
 {
-	if (is_on)
-		s3c_udc_soft_connect();
-	else
-		s3c_udc_soft_disconnect();
+	struct s3c_udc *dev = the_controller;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->soft_connected = is_on;
+	s3c_udc_update_soft_flag();
+	spin_unlock_irqrestore(&dev->lock, flags);
+
 	return 0;
 }
 
@@ -1129,6 +1131,7 @@
 	struct resource *res;
 	unsigned int irq;
 	int retval;
+	u32 tmp;
 
 	DEBUG("%s: %p\n", __func__, pdev);
 
@@ -1176,6 +1179,7 @@
 		goto err_regs_res;
 	}
 
+	dev->phy = usb_get_transceiver();
 	udc_reinit(dev);
 
 	dev->clk = clk_get(&pdev->dev, "usbotg");
@@ -1200,6 +1204,10 @@
 	/* Mask any interrupt left unmasked by the bootloader */
 	__raw_writel(0, dev->regs + S3C_UDC_OTG_GINTMSK);
 
+	/* Stay disconnected until vbus_session is called */
+	tmp = __raw_readl(dev->regs + S3C_UDC_OTG_DCTL);
+	__raw_writel(tmp | SOFT_DISCONNECT, dev->regs + S3C_UDC_OTG_DCTL);
+
 	/* irq setup after old hardware state is cleaned up */
 	irq = platform_get_irq(pdev, 0);
 	retval =
@@ -1228,6 +1236,9 @@
 		goto err_add_udc;
 	}
 
+	if (dev->phy)
+		otg_set_peripheral(dev->phy->otg, &dev->gadget);
+
 	create_proc_files();
 
 	return retval;
@@ -1245,6 +1256,8 @@
 err_regs:
 	iounmap(dev->regs);
 err_regs_res:
+	if (dev->phy)
+		usb_put_transceiver(dev->phy);
 	release_mem_region(res->start, resource_size(res));
 	return retval;
 }
@@ -1261,6 +1274,8 @@
 	usb_del_gadget_udc(&dev->gadget);
 	device_unregister(&dev->gadget.dev);
 
+	if (dev->phy)
+		usb_put_transceiver(dev->phy);
 	clk_put(dev->clk);
 	if (dev->usb_ctrl)
 		dma_free_coherent(&pdev->dev,
@@ -1304,7 +1319,8 @@
 		if (dev->driver->disconnect)
 			dev->driver->disconnect(&dev->gadget);
 
-		udc_disable(dev);
+		if (!dev->phy)
+			udc_disable(dev);
 	}
 
 	return 0;
@@ -1315,9 +1331,10 @@
 	struct s3c_udc *dev = the_controller;
 
 	if (dev->driver) {
-		udc_reinit(dev);
-		udc_enable(dev);
-		s3c_udc_soft_connect();
+		if (!dev->phy) {
+			udc_reinit(dev);
+			udc_enable(dev);
+		}
 
 		if (dev->driver->resume)
 			dev->driver->resume(&dev->gadget);
diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
index 02febbb..b571c2b 100644
--- a/drivers/usb/host/ehci-s5p.c
+++ b/drivers/usb/host/ehci-s5p.c
@@ -30,6 +30,7 @@
 	struct device *dev;
 	struct usb_hcd *hcd;
 	struct clk *clk;
+	struct usb_phy *phy;
 };
 
 static const struct hc_driver s5p_ehci_hc_driver = {
@@ -95,6 +96,7 @@
 
 	s5p_ehci->hcd = hcd;
 	s5p_ehci->clk = clk_get(&pdev->dev, "usbhost");
+	s5p_ehci->phy = usb_get_transceiver();
 
 	if (IS_ERR(s5p_ehci->clk)) {
 		dev_err(&pdev->dev, "Failed to get usbhost clock\n");
@@ -129,7 +131,9 @@
 		goto fail;
 	}
 
-	if (pdata->phy_init)
+	if (s5p_ehci->phy)
+		usb_phy_init(s5p_ehci->phy);
+	else if (pdata->phy_init)
 		pdata->phy_init(pdev, S5P_USB_PHY_HOST);
 
 	ehci = hcd_to_ehci(hcd);
@@ -156,6 +160,14 @@
 
 	platform_set_drvdata(pdev, s5p_ehci);
 
+	if (s5p_ehci->phy) {
+		err = otg_set_host(s5p_ehci->phy->otg, &hcd->self);
+		if (err)
+			goto fail;
+	}
+
+	clk_disable(s5p_ehci->clk);
+
 	return 0;
 
 fail:
@@ -167,6 +179,8 @@
 fail_clk:
 	usb_put_hcd(hcd);
 fail_hcd:
+	if (s5p_ehci->phy)
+		usb_put_transceiver(s5p_ehci->phy);
 	kfree(s5p_ehci);
 	return err;
 }
@@ -179,8 +193,12 @@
 
 	usb_remove_hcd(hcd);
 
-	if (pdata && pdata->phy_exit)
+	if (s5p_ehci->phy) {
+		otg_set_host(s5p_ehci->phy->otg, NULL);
+		usb_put_transceiver(s5p_ehci->phy);
+	} else if (pdata && pdata->phy_exit) {
 		pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
+	}
 
 	iounmap(hcd->regs);
 
@@ -216,6 +234,15 @@
 	unsigned long flags;
 	int rc = 0;
 
+	/*
+	 * If we have an otg driver, the otg wakelock blocks suspend while
+	 * we are in host mode. When you unplug the host cable, the otg driver
+	 * stops the ehci controller and shuts down the phy immediately, so the
+	 * ehci has already been stopped when this function is called.
+	 */
+	if (s5p_ehci->phy)
+		return 0;
+
 	if (time_before(jiffies, ehci->next_statechange))
 		msleep(20);
 
@@ -246,6 +273,9 @@
 	struct platform_device *pdev = to_platform_device(dev);
 	struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
 
+	if (s5p_ehci->phy)
+		return 0;
+
 	if (pdata && pdata->phy_init)
 		pdata->phy_init(pdev, S5P_USB_PHY_HOST);
 
diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c
index b75d19b..328fae6 100644
--- a/drivers/usb/host/ohci-exynos.c
+++ b/drivers/usb/host/ohci-exynos.c
@@ -13,6 +13,7 @@
 
 #include <linux/clk.h>
 #include <linux/platform_device.h>
+#include <linux/usb/otg.h>
 #include <mach/ohci.h>
 #include <plat/usb-phy.h>
 
@@ -20,6 +21,7 @@
 	struct device *dev;
 	struct usb_hcd *hcd;
 	struct clk *clk;
+	struct usb_phy *phy;
 };
 
 static int ohci_exynos_init(struct usb_hcd *hcd)
@@ -113,6 +115,7 @@
 
 	exynos_ohci->hcd = hcd;
 	exynos_ohci->clk = clk_get(&pdev->dev, "usbhost");
+	exynos_ohci->phy = usb_get_transceiver();
 
 	if (IS_ERR(exynos_ohci->clk)) {
 		dev_err(&pdev->dev, "Failed to get usbhost clock\n");
@@ -147,7 +150,9 @@
 		goto fail;
 	}
 
-	if (pdata->phy_init)
+	if (exynos_ohci->phy)
+		usb_phy_init(exynos_ohci->phy);
+	else if (pdata->phy_init)
 		pdata->phy_init(pdev, S5P_USB_PHY_HOST);
 
 	ohci = hcd_to_ohci(hcd);
@@ -159,8 +164,13 @@
 		goto fail;
 	}
 
+	if (exynos_ohci->phy)
+		otg_set_host(exynos_ohci->phy->otg, &hcd->self);
+
 	platform_set_drvdata(pdev, exynos_ohci);
 
+	clk_disable(exynos_ohci->clk);
+
 	return 0;
 
 fail:
@@ -172,6 +182,8 @@
 fail_clk:
 	usb_put_hcd(hcd);
 fail_hcd:
+	if (exynos_ohci->phy)
+		usb_put_transceiver(exynos_ohci->phy);
 	kfree(exynos_ohci);
 	return err;
 }
@@ -184,7 +196,9 @@
 
 	usb_remove_hcd(hcd);
 
-	if (pdata && pdata->phy_exit)
+	if (exynos_ohci->phy)
+		usb_put_transceiver(exynos_ohci->phy);
+	else if (pdata && pdata->phy_exit)
 		pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
 
 	iounmap(hcd->regs);
@@ -221,6 +235,9 @@
 	unsigned long flags;
 	int rc = 0;
 
+	if (exynos_ohci->phy)
+		return 0;
+
 	/*
 	 * Root hub was already suspended. Disable irq emission and
 	 * mark HW unaccessible, bail out if RH has been resumed. Use
@@ -251,6 +268,9 @@
 	struct platform_device *pdev = to_platform_device(dev);
 	struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data;
 
+	if (exynos_ohci->phy)
+		return 0;
+
 	if (pdata && pdata->phy_init)
 		pdata->phy_init(pdev, S5P_USB_PHY_HOST);
 
diff --git a/drivers/usb/otg/otg-wakelock.c b/drivers/usb/otg/otg-wakelock.c
index e17e272..34c3668f 100644
--- a/drivers/usb/otg/otg-wakelock.c
+++ b/drivers/usb/otg/otg-wakelock.c
@@ -90,12 +90,12 @@
 
 	switch (event) {
 	case USB_EVENT_VBUS:
+	case USB_EVENT_ID:
 	case USB_EVENT_ENUMERATED:
 		otgwl_hold(&vbus_lock);
 		break;
 
 	case USB_EVENT_NONE:
-	case USB_EVENT_ID:
 	case USB_EVENT_CHARGER:
 		otgwl_temporary_hold(&vbus_lock);
 		break;
diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c
index cef6557..6b43462 100644
--- a/drivers/video/fbmon.c
+++ b/drivers/video/fbmon.c
@@ -551,6 +551,9 @@
 static void get_detailed_timing(unsigned char *block,
 				struct fb_videomode *mode)
 {
+	int v_size = V_SIZE;
+	int h_size = H_SIZE;
+
 	mode->xres = H_ACTIVE;
 	mode->yres = V_ACTIVE;
 	mode->pixclock = PIXEL_CLOCK;
@@ -579,11 +582,18 @@
 	}
 	mode->flag = FB_MODE_IS_DETAILED;
 
+	/* get aspect ratio */
+	if (h_size * 18 > v_size * 31 && h_size * 18 < v_size * 33)
+		mode->flag |= FB_FLAG_RATIO_16_9;
+	if (h_size * 18 > v_size * 23 && h_size * 18 < v_size * 25)
+		mode->flag |= FB_FLAG_RATIO_4_3;
+
 	DPRINTK("      %d MHz ",  PIXEL_CLOCK/1000000);
 	DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
 	       H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
 	DPRINTK("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,
 	       V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
+	DPRINTK("%dmm %dmm ", H_SIZE, V_SIZE);
 	DPRINTK("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",
 	       (VSYNC_POSITIVE) ? "+" : "-");
 }
@@ -983,17 +993,75 @@
 }
 
 /**
+ * fb_edid_get_cea_sample_rates() - convert from CEA sample rate format
+ * @cea_sample_rates:	sample rate bitfield
+ *
+ * DESCRIPTION:
+ *
+ * This function converts from the sample rates bitfield given in
+ * SAD byte 2 of the Audio Data Block from the CEA EDID Timing
+ * Extension v3 to something that can be understood here.
+ */
+static u8 fb_edid_get_cea_sample_rates(u8 cea_sample_rates)
+{
+	u8 rates = 0;
+
+	if (cea_sample_rates & (1 << 0))
+		rates |= FB_AUDIO_32KHZ;
+	if (cea_sample_rates & (1 << 1))
+		rates |= FB_AUDIO_44KHZ;
+	if (cea_sample_rates & (1 << 2))
+		rates |= FB_AUDIO_48KHZ;
+	if (cea_sample_rates & (1 << 3))
+		rates |= FB_AUDIO_88KHZ;
+	if (cea_sample_rates & (1 << 4))
+		rates |= FB_AUDIO_96KHZ;
+	if (cea_sample_rates & (1 << 5))
+		rates |= FB_AUDIO_176KHZ;
+	if (cea_sample_rates & (1 << 6))
+		rates |= FB_AUDIO_192KHZ;
+
+	return rates;
+}
+
+/**
+ * fb_edid_get_cea_bit_rates() - convert from CEA bit rate format
+ * @cea_bit_rates:	bit rate bitfield
+ *
+ * DESCRIPTION:
+ *
+ * This function converts from the bit rate bitfield given in
+ * SAD byte 3 of the Audio Data Block from the CEA EDID Timing
+ * Extension v3 to something that can be understood here.
+ */
+static u8 fb_edid_get_cea_bit_rates(u8 cea_bit_rates)
+{
+	u8 rates = 0;
+
+	if (cea_bit_rates & (1 << 0))
+		rates |= FB_AUDIO_16BIT;
+	if (cea_bit_rates & (1 << 1))
+		rates |= FB_AUDIO_20BIT;
+	if (cea_bit_rates & (1 << 2))
+		rates |= FB_AUDIO_24BIT;
+
+	return rates;
+}
+
+/**
  * fb_edid_add_monspecs() - add monitor video modes from E-EDID data
  * @edid:	128 byte array with an E-EDID block
- * @spacs:	monitor specs to be extended
+ * @specs:	monitor specs to be extended
  */
 void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs)
 {
 	unsigned char *block;
 	struct fb_videomode *m;
+	struct fb_audio *audiodb;
 	int num = 0, i;
-	u8 svd[64], edt[(128 - 4) / DETAILED_TIMING_DESCRIPTION_SIZE];
-	u8 pos = 4, svd_n = 0;
+	u8 sad[128 - 5], svd[64];
+	u8 edt[(128 - 4) / DETAILED_TIMING_DESCRIPTION_SIZE];
+	u8 pos = 4, sad_n = 0, svd_n = 0;
 
 	if (!edid)
 		return;
@@ -1001,23 +1069,74 @@
 	if (!edid_checksum(edid))
 		return;
 
-	if (edid[0] != 0x2 ||
+	if (edid[0] != 0x2 || edid[1] != 0x3 ||
 	    edid[2] < 4 || edid[2] > 128 - DETAILED_TIMING_DESCRIPTION_SIZE)
 		return;
 
-	DPRINTK("  Short Video Descriptors\n");
+	DPRINTK("  Data Block Collection\n");
 
 	while (pos < edid[2]) {
 		u8 len = edid[pos] & 0x1f, type = (edid[pos] >> 5) & 7;
 		pr_debug("Data block %u of %u bytes\n", type, len);
-		if (type == 2)
+
+		if (len == 0)
+			break;
+
+		pos++;
+		if (type == 1) {
+			/* Short Audio Descriptors */
+			for (i = pos; i < pos + len; i += 3) {
+				if (((edid[i] >> 3) & 0xf) != 1)
+					continue; /* skip non-lpcm */
+
+				pr_debug("LPCM ch=%d\n", (edid[i] & 7) + 1);
+
+				sad[sad_n++] = (edid[i] & 7) + 1;
+				sad[sad_n++] = edid[i + 1];
+				sad[sad_n++] = edid[i + 2];
+			}
+		} else if (type == 2) {
+			/* Short Video Descriptors */
 			for (i = pos; i < pos + len; i++) {
-				u8 idx = edid[pos + i] & 0x7f;
+				u8 idx = edid[i] & 0x7f;
 				svd[svd_n++] = idx;
 				pr_debug("N%sative mode #%d\n",
-					 edid[pos + i] & 0x80 ? "" : "on-n", idx);
+					 edid[i] & 0x80 ? "" : "on-n", idx);
 			}
-		pos += len + 1;
+		} else if (type == 3 && len >= 3) {
+			/* Vendor block */
+			u32 ieee_reg = edid[pos] | (edid[pos + 1] << 8) |
+				(edid[pos + 2] << 16);
+			if (ieee_reg == 0x000c03)
+				specs->misc |= FB_MISC_HDMI;
+		}
+
+		pos += len;
+	}
+
+	if (sad_n > 0) {
+		/* Short audio descriptors are in blocks of 3 bytes */
+		sad_n /= 3;
+		pr_debug("Found %d lpcm audio blocks\n", sad_n);
+		audiodb = kzalloc(sad_n * sizeof(struct fb_audio), GFP_KERNEL);
+		if (!audiodb)
+			return;
+
+		for (i = 0; i < sad_n; i++) {
+			audiodb[i].format = FB_AUDIO_LPCM;
+			audiodb[i].channel_count = sad[i * 3];
+			audiodb[i].sample_rates =
+				fb_edid_get_cea_sample_rates(sad[i * 3 + 1]);
+			audiodb[i].bit_rates =
+				fb_edid_get_cea_bit_rates(sad[i * 3 + 2]);
+		}
+
+		kfree(specs->audiodb);
+		specs->audiodb = audiodb;
+		specs->audiodb_len = sad_n;
+	} else {
+		kfree(specs->audiodb);
+		specs->audiodb_len = 0;
 	}
 
 	block = edid + edid[2];
@@ -1029,7 +1148,7 @@
 		if (PIXEL_CLOCK)
 			edt[num++] = block - edid;
 
-	/* Yikes, EDID data is totally useless */
+	/* No video descriptors, so nothing more to do */
 	if (!(num + svd_n))
 		return;
 
@@ -1050,10 +1169,8 @@
 
 	for (i = specs->modedb_len + num; i < specs->modedb_len + num + svd_n; i++) {
 		int idx = svd[i - specs->modedb_len - num];
-		if (!idx || idx > 63) {
+		if (!idx || idx > (CEA_MODEDB_SIZE - 1)) {
 			pr_warning("Reserved SVD code %d\n", idx);
-		} else if (idx > ARRAY_SIZE(cea_modes) || !cea_modes[idx].xres) {
-			pr_warning("Unimplemented SVD code %d\n", idx);
 		} else {
 			memcpy(&m[i], cea_modes + idx, sizeof(m[i]));
 			pr_debug("Adding SVD #%d: %ux%u@%u\n", idx,
diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c
index a9a907c..d67f8a6 100644
--- a/drivers/video/modedb.c
+++ b/drivers/video/modedb.c
@@ -292,64 +292,524 @@
 };
 
 #ifdef CONFIG_FB_MODE_HELPERS
-const struct fb_videomode cea_modes[64] = {
-	/* #1: 640x480p@59.94/60Hz */
-	[1] = {
-		NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0,
-		FB_VMODE_NONINTERLACED, 0,
-	},
-	/* #3: 720x480p@59.94/60Hz */
-	[3] = {
-		NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
-		FB_VMODE_NONINTERLACED, 0,
-	},
-	/* #5: 1920x1080i@59.94/60Hz */
-	[5] = {
-		NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5,
-		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-		FB_VMODE_INTERLACED, 0,
-	},
-	/* #7: 720(1440)x480iH@59.94/60Hz */
-	[7] = {
-		NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
-		FB_VMODE_INTERLACED, 0,
-	},
-	/* #9: 720(1440)x240pH@59.94/60Hz */
-	[9] = {
-		NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0,
-		FB_VMODE_NONINTERLACED, 0,
-	},
-	/* #18: 720x576pH@50Hz */
-	[18] = {
-		NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
-		FB_VMODE_NONINTERLACED, 0,
-	},
-	/* #19: 1280x720p@50Hz */
-	[19] = {
-		NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5,
-		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-		FB_VMODE_NONINTERLACED, 0,
-	},
-	/* #20: 1920x1080i@50Hz */
-	[20] = {
-		NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5,
-		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-		FB_VMODE_INTERLACED, 0,
-	},
-	/* #32: 1920x1080p@23.98/24Hz */
-	[32] = {
-		NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5,
-		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-		FB_VMODE_NONINTERLACED, 0,
-	},
-	/* #35: (2880)x480p4x@59.94/60Hz */
-	[35] = {
-		NULL, 60, 2880, 480, 9250, 240, 64, 30, 9, 248, 6, 0,
-		FB_VMODE_NONINTERLACED, 0,
-	},
+const struct fb_videomode cea_modes[CEA_MODEDB_SIZE] = {
+	{},
+	/* 1: 640x480p @ 59.94Hz/60Hz */
+	{.refresh = 59, .xres = 640, .yres = 480, .pixclock = 39721,
+	 .left_margin = 48, .right_margin = 16,
+	 .upper_margin = 33, .lower_margin = 1,
+	 .hsync_len = 96, .vsync_len = 2,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 2: 720x480p @ 59.94Hz/60Hz */
+	{.refresh = 59, .xres = 720, .yres = 480, .pixclock = 37037,
+	 .left_margin = 60, .right_margin = 16,
+	 .upper_margin = 30, .lower_margin = 9,
+	 .hsync_len = 62, .vsync_len = 6,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 3: 720x480p @ 59.94Hz/60Hz */
+	{.refresh = 59, .xres = 720, .yres = 480, .pixclock = 37037,
+	 .left_margin = 60, .right_margin = 16,
+	 .upper_margin = 30, .lower_margin = 9,
+	 .hsync_len = 62, .vsync_len = 6,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 4: 1280x720p @ 59.94Hz/60Hz */
+	{.refresh = 60, .xres = 1280, .yres = 720, .pixclock = 13468,
+	 .left_margin = 220, .right_margin = 110,
+	 .upper_margin = 20, .lower_margin = 5,
+	 .hsync_len = 40, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 5: 1920x1080i @ 59.94Hz/60Hz */
+	{.refresh = 60, .xres = 1920, .yres = 1080, .pixclock = 13468,
+	 .left_margin = 148, .right_margin = 88,
+	 .upper_margin = 15, .lower_margin = 2,
+	 .hsync_len = 44, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 6: 720(1440)x480i @ 59.94Hz/60Hz */
+	{.refresh = 59, .xres = 1440, .yres = 480, .pixclock = 37037,
+	 .left_margin = 114, .right_margin = 38,
+	 .upper_margin = 15, .lower_margin = 4,
+	 .hsync_len = 124, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 7: 720(1440)x480i @ 59.94Hz/60Hz */
+	{.refresh = 59, .xres = 1440, .yres = 480, .pixclock = 37037,
+	 .left_margin = 114, .right_margin = 38,
+	 .upper_margin = 15, .lower_margin = 4,
+	 .hsync_len = 124, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 8: 720(1440)x240p @ 59.94Hz/60Hz */
+	{.refresh = 59, .xres = 1440, .yres = 240, .pixclock = 37037,
+	 .left_margin = 114, .right_margin = 38,
+	 .upper_margin = 15, .lower_margin = 5,
+	 .hsync_len = 124, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 9: 720(1440)x240p @ 59.94Hz/60Hz */
+	{.refresh = 59, .xres = 1440, .yres = 240, .pixclock = 37037,
+	 .left_margin = 114, .right_margin = 38,
+	 .upper_margin = 15, .lower_margin = 5,
+	 .hsync_len = 124, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 10: 2880x480i @ 59.94Hz/60Hz */
+	{.refresh = 59, .xres = 2880, .yres = 480, .pixclock = 18518,
+	 .left_margin = 228, .right_margin = 76,
+	 .upper_margin = 15, .lower_margin = 4,
+	 .hsync_len = 248, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 11: 2880x480i @ 59.94Hz/60Hz */
+	{.refresh = 59, .xres = 2880, .yres = 480, .pixclock = 18518,
+	 .left_margin = 228, .right_margin = 76,
+	 .upper_margin = 15, .lower_margin = 4,
+	 .hsync_len = 248, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 12: 2880x240p @ 59.94Hz/60Hz */
+	{.refresh = 59, .xres = 2880, .yres = 240, .pixclock = 18518,
+	 .left_margin = 228, .right_margin = 76,
+	 .upper_margin = 15, .lower_margin = 5,
+	 .hsync_len = 248, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 13: 2880x240p @ 59.94Hz/60Hz */
+	{.refresh = 59, .xres = 2880, .yres = 240, .pixclock = 18518,
+	 .left_margin = 228, .right_margin = 76,
+	 .upper_margin = 15, .lower_margin = 5,
+	 .hsync_len = 248, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 14: 1440x480p @ 59.94Hz/60Hz */
+	{.refresh = 59, .xres = 1440, .yres = 480, .pixclock = 18518,
+	 .left_margin = 120, .right_margin = 32,
+	 .upper_margin = 30, .lower_margin = 9,
+	 .hsync_len = 124, .vsync_len = 6,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 15: 1440x480p @ 59.94Hz/60Hz */
+	{.refresh = 59, .xres = 1440, .yres = 480, .pixclock = 18518,
+	 .left_margin = 120, .right_margin = 32,
+	 .upper_margin = 30, .lower_margin = 9,
+	 .hsync_len = 124, .vsync_len = 6,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 16: 1920x1080p @ 59.94Hz/60Hz */
+	{.refresh = 60, .xres = 1920, .yres = 1080, .pixclock = 6734,
+	 .left_margin = 148, .right_margin = 88,
+	 .upper_margin = 36, .lower_margin = 4,
+	 .hsync_len = 44, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 17: 720x576p @ 50Hz */
+	{.refresh = 50, .xres = 720, .yres = 576, .pixclock = 37037,
+	 .left_margin = 68, .right_margin = 12,
+	 .upper_margin = 39, .lower_margin = 5,
+	 .hsync_len = 64, .vsync_len = 5,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 18: 720x576p @ 50Hz */
+	{.refresh = 50, .xres = 720, .yres = 576, .pixclock = 37037,
+	 .left_margin = 68, .right_margin = 12,
+	 .upper_margin = 39, .lower_margin = 5,
+	 .hsync_len = 64, .vsync_len = 5,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 19: 1280x720p @ 50Hz */
+	{.refresh = 50, .xres = 1280, .yres = 720, .pixclock = 13468,
+	 .left_margin = 220, .right_margin = 440,
+	 .upper_margin = 20, .lower_margin = 5,
+	 .hsync_len = 40, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 20: 1920x1080i @ 50Hz */
+	{.refresh = 50, .xres = 1920, .yres = 1080, .pixclock = 13468,
+	 .left_margin = 148, .right_margin = 528,
+	 .upper_margin = 15, .lower_margin = 2,
+	 .hsync_len = 44, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 21: 720(1440)x576i @ 50Hz */
+	{.refresh = 50, .xres = 1440, .yres = 576, .pixclock = 37037,
+	 .left_margin = 138, .right_margin = 24,
+	 .upper_margin = 19, .lower_margin = 2,
+	 .hsync_len = 126, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 22: 720(1440)x576i @ 50Hz */
+	{.refresh = 50, .xres = 1440, .yres = 576, .pixclock = 37037,
+	 .left_margin = 138, .right_margin = 24,
+	 .upper_margin = 19, .lower_margin = 2,
+	 .hsync_len = 126, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 23: 720(1440)x288p @ 50Hz */
+	{.refresh = 49, .xres = 1440, .yres = 288, .pixclock = 37037,
+	 .left_margin = 138, .right_margin = 24,
+	 .upper_margin = 19, .lower_margin = 4,
+	 .hsync_len = 126, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 24: 720(1440)x288p @ 50Hz */
+	{.refresh = 49, .xres = 1440, .yres = 288, .pixclock = 37037,
+	 .left_margin = 138, .right_margin = 24,
+	 .upper_margin = 19, .lower_margin = 4,
+	 .hsync_len = 126, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 25: 2880x576i @ 50Hz */
+	{.refresh = 50, .xres = 2880, .yres = 576, .pixclock = 18518,
+	 .left_margin = 276, .right_margin = 48,
+	 .upper_margin = 19, .lower_margin = 2,
+	 .hsync_len = 252, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 26: 2880x576i @ 50Hz */
+	{.refresh = 50, .xres = 2880, .yres = 576, .pixclock = 18518,
+	 .left_margin = 276, .right_margin = 48,
+	 .upper_margin = 19, .lower_margin = 2,
+	 .hsync_len = 252, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 27: 2880x288p @ 50Hz */
+	{.refresh = 49, .xres = 2880, .yres = 288, .pixclock = 18518,
+	 .left_margin = 276, .right_margin = 48,
+	 .upper_margin = 19, .lower_margin = 4,
+	 .hsync_len = 252, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 28: 2880x288p @ 50Hz */
+	{.refresh = 49, .xres = 2880, .yres = 288, .pixclock = 18518,
+	 .left_margin = 276, .right_margin = 48,
+	 .upper_margin = 19, .lower_margin = 4,
+	 .hsync_len = 252, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 29: 1440x576p @ 50Hz */
+	{.refresh = 50, .xres = 1440, .yres = 576, .pixclock = 18518,
+	 .left_margin = 136, .right_margin = 24,
+	 .upper_margin = 39, .lower_margin = 5,
+	 .hsync_len = 128, .vsync_len = 5,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 30: 1440x576p @ 50Hz */
+	{.refresh = 50, .xres = 1440, .yres = 576, .pixclock = 18518,
+	 .left_margin = 136, .right_margin = 24,
+	 .upper_margin = 39, .lower_margin = 5,
+	 .hsync_len = 128, .vsync_len = 5,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 31: 1920x1080p @ 50Hz */
+	{.refresh = 50, .xres = 1920, .yres = 1080, .pixclock = 6734,
+	 .left_margin = 148, .right_margin = 528,
+	 .upper_margin = 36, .lower_margin = 4,
+	 .hsync_len = 44, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 32: 1920x1080p @ 23.97Hz/24Hz */
+	{.refresh = 24, .xres = 1920, .yres = 1080, .pixclock = 13468,
+	 .left_margin = 148, .right_margin = 638,
+	 .upper_margin = 36, .lower_margin = 4,
+	 .hsync_len = 44, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 33: 1920x1080p @ 25Hz */
+	{.refresh = 25, .xres = 1920, .yres = 1080, .pixclock = 13468,
+	 .left_margin = 148, .right_margin = 528,
+	 .upper_margin = 36, .lower_margin = 4,
+	 .hsync_len = 44, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 34: 1920x1080p @ 29.97Hz/30Hz */
+	{.refresh = 30, .xres = 1920, .yres = 1080, .pixclock = 13468,
+	 .left_margin = 148, .right_margin = 88,
+	 .upper_margin = 36, .lower_margin = 4,
+	 .hsync_len = 44, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 35: 2880x480p @ 59.94Hz/60Hz */
+	{.refresh = 59, .xres = 2880, .yres = 480, .pixclock = 9259,
+	 .left_margin = 240, .right_margin = 64,
+	 .upper_margin = 30, .lower_margin = 9,
+	 .hsync_len = 248, .vsync_len = 6,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 36: 2880x480p @ 59.94Hz/60Hz */
+	{.refresh = 59, .xres = 2880, .yres = 480, .pixclock = 9259,
+	 .left_margin = 240, .right_margin = 64,
+	 .upper_margin = 30, .lower_margin = 9,
+	 .hsync_len = 248, .vsync_len = 6,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 37: 2880x576p @ 50Hz */
+	{.refresh = 50, .xres = 2880, .yres = 576, .pixclock = 9259,
+	 .left_margin = 272, .right_margin = 48,
+	 .upper_margin = 39, .lower_margin = 5,
+	 .hsync_len = 256, .vsync_len = 5,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 38: 2880x576p @ 50Hz */
+	{.refresh = 50, .xres = 2880, .yres = 576, .pixclock = 9259,
+	 .left_margin = 272, .right_margin = 48,
+	 .upper_margin = 39, .lower_margin = 5,
+	 .hsync_len = 256, .vsync_len = 5,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 39: 1920x1080i @ 50Hz */
+	{.refresh = 50, .xres = 1920, .yres = 1080, .pixclock = 13888,
+	 .left_margin = 184, .right_margin = 32,
+	 .upper_margin = 57, .lower_margin = 2,
+	 .hsync_len = 168, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 40: 1920x1080i @ 100Hz */
+	{.refresh = 100, .xres = 1920, .yres = 1080, .pixclock = 6734,
+	 .left_margin = 148, .right_margin = 528,
+	 .upper_margin = 15, .lower_margin = 2,
+	 .hsync_len = 44, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 41: 1280x720p @ 100Hz */
+	{.refresh = 100, .xres = 1280, .yres = 720, .pixclock = 6734,
+	 .left_margin = 220, .right_margin = 440,
+	 .upper_margin = 20, .lower_margin = 5,
+	 .hsync_len = 40, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 42: 720x576p @ 100Hz */
+	{.refresh = 100, .xres = 720, .yres = 576, .pixclock = 18518,
+	 .left_margin = 68, .right_margin = 12,
+	 .upper_margin = 39, .lower_margin = 5,
+	 .hsync_len = 64, .vsync_len = 5,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 43: 720x576p @ 100Hz */
+	{.refresh = 100, .xres = 720, .yres = 576, .pixclock = 18518,
+	 .left_margin = 68, .right_margin = 12,
+	 .upper_margin = 39, .lower_margin = 5,
+	 .hsync_len = 64, .vsync_len = 5,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 44: 720(1440)x576i @ 100Hz */
+	{.refresh = 100, .xres = 1440, .yres = 576, .pixclock = 18518,
+	 .left_margin = 138, .right_margin = 24,
+	 .upper_margin = 19, .lower_margin = 2,
+	 .hsync_len = 126, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 45: 720(1440)x576i @ 100Hz */
+	{.refresh = 100, .xres = 1440, .yres = 576, .pixclock = 18518,
+	 .left_margin = 138, .right_margin = 24,
+	 .upper_margin = 19, .lower_margin = 2,
+	 .hsync_len = 126, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 46: 1920x1080i @ 119.88/120Hz */
+	{.refresh = 120, .xres = 1920, .yres = 1080, .pixclock = 6734,
+	 .left_margin = 148, .right_margin = 88,
+	 .upper_margin = 15, .lower_margin = 2,
+	 .hsync_len = 44, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 47: 1280x720p @ 119.88/120Hz */
+	{.refresh = 120, .xres = 1280, .yres = 720, .pixclock = 6734,
+	 .left_margin = 220, .right_margin = 110,
+	 .upper_margin = 20, .lower_margin = 5,
+	 .hsync_len = 40, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 48: 720x480p @ 119.88/120Hz */
+	{.refresh = 119, .xres = 720, .yres = 480, .pixclock = 18518,
+	 .left_margin = 60, .right_margin = 16,
+	 .upper_margin = 30, .lower_margin = 9,
+	 .hsync_len = 62, .vsync_len = 6,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 49: 720x480p @ 119.88/120Hz */
+	{.refresh = 119, .xres = 720, .yres = 480, .pixclock = 18518,
+	 .left_margin = 60, .right_margin = 16,
+	 .upper_margin = 30, .lower_margin = 9,
+	 .hsync_len = 62, .vsync_len = 6,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 50: 720(1440)x480i @ 119.88/120Hz */
+	{.refresh = 119, .xres = 1440, .yres = 480, .pixclock = 18518,
+	 .left_margin = 114, .right_margin = 38,
+	 .upper_margin = 15, .lower_margin = 4,
+	 .hsync_len = 124, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 51: 720(1440)x480i @ 119.88/120Hz */
+	{.refresh = 119, .xres = 1440, .yres = 480, .pixclock = 18518,
+	 .left_margin = 114, .right_margin = 38,
+	 .upper_margin = 15, .lower_margin = 4,
+	 .hsync_len = 124, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 52: 720x576p @ 200Hz */
+	{.refresh = 200, .xres = 720, .yres = 576, .pixclock = 9259,
+	 .left_margin = 68, .right_margin = 12,
+	 .upper_margin = 39, .lower_margin = 5,
+	 .hsync_len = 64, .vsync_len = 5,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 53: 720x576p @ 200Hz */
+	{.refresh = 200, .xres = 720, .yres = 576, .pixclock = 9259,
+	 .left_margin = 68, .right_margin = 12,
+	 .upper_margin = 39, .lower_margin = 5,
+	 .hsync_len = 64, .vsync_len = 5,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 54: 720(1440)x576i @ 200Hz */
+	{.refresh = 200, .xres = 1440, .yres = 576, .pixclock = 9259,
+	 .left_margin = 138, .right_margin = 24,
+	 .upper_margin = 19, .lower_margin = 2,
+	 .hsync_len = 126, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 55: 720(1440)x576i @ 200Hz */
+	{.refresh = 200, .xres = 1440, .yres = 576, .pixclock = 9259,
+	 .left_margin = 138, .right_margin = 24,
+	 .upper_margin = 19, .lower_margin = 2,
+	 .hsync_len = 126, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 56: 720x480p @ 239.76/240Hz */
+	{.refresh = 239, .xres = 720, .yres = 480, .pixclock = 9259,
+	 .left_margin = 60, .right_margin = 16,
+	 .upper_margin = 30, .lower_margin = 9,
+	 .hsync_len = 62, .vsync_len = 6,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 57: 720x480p @ 239.76/240Hz */
+	{.refresh = 239, .xres = 720, .yres = 480, .pixclock = 9259,
+	 .left_margin = 60, .right_margin = 16,
+	 .upper_margin = 30, .lower_margin = 9,
+	 .hsync_len = 62, .vsync_len = 6,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 58: 720(1440)x480i @ 239.76/240Hz */
+	{.refresh = 239, .xres = 1440, .yres = 480, .pixclock = 9259,
+	 .left_margin = 114, .right_margin = 38,
+	 .upper_margin = 15, .lower_margin = 4,
+	 .hsync_len = 124, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_4_3 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 59: 720(1440)x480i @ 239.76/240Hz */
+	{.refresh = 239, .xres = 1440, .yres = 480, .pixclock = 9259,
+	 .left_margin = 114, .right_margin = 38,
+	 .upper_margin = 15, .lower_margin = 4,
+	 .hsync_len = 124, .vsync_len = 3,
+	 .sync = 0,
+	 .flag = FB_FLAG_RATIO_16_9 | FB_FLAG_PIXEL_REPEAT,
+	 .vmode = FB_VMODE_INTERLACED},
+	/* 60: 1280x720p @ 23.97Hz/24Hz */
+	{.refresh = 24, .xres = 1280, .yres = 720, .pixclock = 16835,
+	 .left_margin = 220, .right_margin = 1760,
+	 .upper_margin = 20, .lower_margin = 5,
+	 .hsync_len = 40, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 61: 1280x720p @ 25Hz */
+	{.refresh = 25, .xres = 1280, .yres = 720, .pixclock = 13468,
+	 .left_margin = 220, .right_margin = 2420,
+	 .upper_margin = 20, .lower_margin = 5,
+	 .hsync_len = 40, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 62: 1280x720p @ 29.97Hz/30Hz */
+	{.refresh = 30, .xres = 1280, .yres = 720, .pixclock = 13468,
+	 .left_margin = 220, .right_margin = 1760,
+	 .upper_margin = 20, .lower_margin = 5,
+	 .hsync_len = 40, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 63: 1920x1080p @ 119.88/120Hz */
+	{.refresh = 120, .xres = 1920, .yres = 1080, .pixclock = 3367,
+	 .left_margin = 148, .right_margin = 88,
+	 .upper_margin = 36, .lower_margin = 4,
+	 .hsync_len = 44, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
+	/* 64: 1920x1080p @ 100Hz */
+	{.refresh = 100, .xres = 1920, .yres = 1080, .pixclock = 3367,
+	 .left_margin = 148, .right_margin = 528,
+	 .upper_margin = 36, .lower_margin = 4,
+	 .hsync_len = 44, .vsync_len = 5,
+	 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	 .flag = FB_FLAG_RATIO_16_9,
+	 .vmode = FB_VMODE_NONINTERLACED},
 };
+EXPORT_SYMBOL(cea_modes);
 
-const struct fb_videomode vesa_modes[] = {
+const struct fb_videomode vesa_modes[VESA_MODEDB_SIZE] = {
 	/* 0 640x350-85 VESA */
 	{ NULL, 85, 640, 350, 31746,  96, 32, 60, 32, 64, 3,
 	  FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA},
diff --git a/drivers/video/s5p-dp-core.c b/drivers/video/s5p-dp-core.c
index 7135bee..ea3eae3 100644
--- a/drivers/video/s5p-dp-core.c
+++ b/drivers/video/s5p-dp-core.c
@@ -113,8 +113,8 @@
 		}
 		sum = s5p_dp_calc_edid_check_sum(edid);
 		if (sum != 0) {
-			dev_err(dp->dev, "EDID bad checksum!\n");
-			return -EIO;
+			dev_warn(dp->dev, "EDID bad checksum!\n");
+			return 0;
 		}
 
 		/* Read additional EDID data */
@@ -129,8 +129,8 @@
 		}
 		sum = s5p_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]);
 		if (sum != 0) {
-			dev_err(dp->dev, "EDID bad checksum!\n");
-			return -EIO;
+			dev_warn(dp->dev, "EDID bad checksum!\n");
+			return 0;
 		}
 
 		retval = s5p_dp_read_byte_from_dpcd(dp,
@@ -172,8 +172,8 @@
 		}
 		sum = s5p_dp_calc_edid_check_sum(edid);
 		if (sum != 0) {
-			dev_err(dp->dev, "EDID bad checksum!\n");
-			return -EIO;
+			dev_warn(dp->dev, "EDID bad checksum!\n");
+			return 0;
 		}
 
 		retval = s5p_dp_read_byte_from_dpcd(dp,
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig
index eb9e376..966501e 100644
--- a/drivers/w1/slaves/Kconfig
+++ b/drivers/w1/slaves/Kconfig
@@ -94,6 +94,19 @@
 
 	  If you are unsure, say N.
 
+config W1_SLAVE_DS2784
+	tristate "Dallas 2784 fuel gauge chip"
+	depends on W1
+	help
+	  If you enable this you will have DS2784 fuel gauge
+	  chip support.
+
+	  This fuel gauge chip is used to monitor remaining battery
+	  capacity, and possibly protect against charging errors, for
+	  Li+ batteries.
+
+	  If you are unsure, say N.
+
 config W1_SLAVE_BQ27000
 	tristate "BQ27000 slave support"
 	depends on W1
diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile
index c4f1859..5c5720e 100644
--- a/drivers/w1/slaves/Makefile
+++ b/drivers/w1/slaves/Makefile
@@ -11,4 +11,5 @@
 obj-$(CONFIG_W1_SLAVE_DS2760)	+= w1_ds2760.o
 obj-$(CONFIG_W1_SLAVE_DS2780)	+= w1_ds2780.o
 obj-$(CONFIG_W1_SLAVE_DS2781)	+= w1_ds2781.o
+obj-$(CONFIG_W1_SLAVE_DS2784)	+= w1_ds2784.o
 obj-$(CONFIG_W1_SLAVE_BQ27000)	+= w1_bq27000.o
diff --git a/drivers/w1/slaves/w1_ds2784.c b/drivers/w1/slaves/w1_ds2784.c
new file mode 100644
index 0000000..2df25aa
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2784.c
@@ -0,0 +1,118 @@
+/*
+ * 1-Wire implementation for the ds2784 chip
+ *
+ * Copyright (C) 2012 Invensense, Inc.
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_family.h"
+#include "w1_ds2784.h"
+
+static int w1_ds2784_io(struct device *dev, char *buf, int addr, size_t count,
+			int io)
+{
+	struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+	mutex_lock(&sl->master->mutex);
+
+	if (addr > DS2784_DATA_SIZE || addr < 0) {
+		count = 0;
+		goto out;
+	}
+	if (addr + count > DS2784_DATA_SIZE)
+		count = DS2784_DATA_SIZE - addr;
+
+	if (!w1_reset_select_slave(sl)) {
+		if (!io) {
+			w1_write_8(sl->master, W1_DS2784_READ_DATA);
+			w1_write_8(sl->master, addr);
+			count = w1_read_block(sl->master, buf, count);
+		} else {
+			w1_write_8(sl->master, W1_DS2784_WRITE_DATA);
+			w1_write_8(sl->master, addr);
+			w1_write_block(sl->master, buf, count);
+		}
+	}
+
+out:
+	mutex_unlock(&sl->master->mutex);
+
+	return count;
+}
+
+int w1_ds2784_read(struct device *dev, char *buf, int addr, size_t count)
+{
+	return w1_ds2784_io(dev, buf, addr, count, 0);
+}
+EXPORT_SYMBOL(w1_ds2784_read);
+
+static int w1_ds2784_add_slave(struct w1_slave *sl)
+{
+	struct platform_device *pdev;
+	int ret;
+
+	pdev = platform_device_alloc("ds2784-fuelgauge", -1);
+	if (!pdev)
+		return -ENOMEM;
+
+	pdev->dev.parent = &sl->dev;
+	ret = platform_device_add(pdev);
+
+	if (ret)
+		goto add_failed;
+
+	dev_set_drvdata(&sl->dev, pdev);
+	return 0;
+
+add_failed:
+	platform_device_unregister(pdev);
+	return ret;
+}
+
+static void w1_ds2784_remove_slave(struct w1_slave *sl)
+{
+	struct platform_device *pdev = dev_get_drvdata(&sl->dev);
+
+	platform_device_unregister(pdev);
+}
+
+static struct w1_family_ops w1_ds2784_fops = {
+	.add_slave = w1_ds2784_add_slave,
+	.remove_slave = w1_ds2784_remove_slave,
+};
+
+static struct w1_family w1_ds2784_family = {
+	.fid = W1_FAMILY_DS2784,
+	.fops = &w1_ds2784_fops,
+};
+
+int __init w1_ds2784_init(void)
+{
+	return w1_register_family(&w1_ds2784_family);
+}
+
+static void __exit w1_ds2784_exit(void)
+{
+	w1_unregister_family(&w1_ds2784_family);
+}
+
+module_init(w1_ds2784_init);
+module_exit(w1_ds2784_exit);
+
+MODULE_AUTHOR("Samsung");
+MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2784 Fuel Gauge IC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/w1/slaves/w1_ds2784.h b/drivers/w1/slaves/w1_ds2784.h
new file mode 100644
index 0000000..9b17453
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2784.h
@@ -0,0 +1,92 @@
+/*
+ * 1-Wire implementation for the ds2784 chip
+ *
+ * Copyright (C) 2012 Invensense, Inc.
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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 _W1_DS2784_H
+#define _W1_DS2784_H
+
+#define W1_DS2784_READ_DATA		0x69
+#define W1_DS2784_WRITE_DATA		0x6C
+
+#define DS2784_REG_PORT                 0x00
+#define DS2784_REG_STS                  0x01
+#define DS2784_REG_RAAC_MSB             0x02
+#define DS2784_REG_RAAC_LSB             0x03
+#define DS2784_REG_RSAC_MSB             0x04
+#define DS2784_REG_RSAC_LSB             0x05
+#define DS2784_REG_RARC                 0x06
+#define DS2784_REG_RSRC                 0x07
+#define DS2784_REG_AVG_CURR_MSB         0x08
+#define DS2784_REG_AVG_CURR_LSB         0x09
+#define DS2784_REG_TEMP_MSB             0x0A
+#define DS2784_REG_TEMP_LSB             0x0B
+#define DS2784_REG_VOLT_MSB             0x0C
+#define DS2784_REG_VOLT_LSB             0x0D
+#define DS2784_REG_CURR_MSB             0x0E
+#define DS2784_REG_CURR_LSB             0x0F
+#define DS2784_REG_ACCUMULATE_CURR_MSB  0x10
+#define DS2784_REG_ACCUMULATE_CURR_LSB  0x11
+#define DS2784_REG_ACCUMULATE_CURR_LSB1 0x12
+#define DS2784_REG_ACCUMULATE_CURR_LSB2 0x13
+#define DS2784_REG_AGE_SCALAR           0x14
+#define DS2784_REG_SPECIALL_FEATURE     0x15
+#define DS2784_REG_FULL_MSB             0x16
+#define DS2784_REG_FULL_LSB             0x17
+#define DS2784_REG_ACTIVE_EMPTY_MSB     0x18
+#define DS2784_REG_ACTIVE_EMPTY_LSB     0x19
+#define DS2784_REG_STBY_EMPTY_MSB       0x1A
+#define DS2784_REG_STBY_EMPTY_LSB       0x1B
+#define DS2784_REG_EEPROM               0x1F
+#define DS2784_REG_MFG_GAIN_RSGAIN_MSB  0xB0
+#define DS2784_REG_MFG_GAIN_RSGAIN_LSB  0xB1
+
+#define DS2784_REG_CTRL                 0x60
+#define DS2784_REG_ACCUMULATE_BIAS      0x61
+#define DS2784_REG_AGE_CAPA_MSB         0x62
+#define DS2784_REG_AGE_CAPA_LSB         0x63
+#define DS2784_REG_CHARGE_VOLT          0x64
+#define DS2784_REG_MIN_CHARGE_CURR      0x65
+#define DS2784_REG_ACTIVE_EMPTY_VOLT    0x66
+#define DS2784_REG_ACTIVE_EMPTY_CURR    0x67
+#define DS2784_REG_ACTIVE_EMPTY_40      0x68
+#define DS2784_REG_RSNSP                0x69
+#define DS2784_REG_FULL_40_MSB          0x6A
+#define DS2784_REG_FULL_40_LSB          0x6B
+#define DS2784_REG_FULL_SEG_4_SLOPE     0x6C
+#define DS2784_REG_FULL_SEG_3_SLOPE     0x6D
+#define DS2784_REG_FULL_SEG_2_SLOPE     0x6E
+#define DS2784_REG_FULL_SEG_1_SLOPE     0x6F
+#define DS2784_REG_AE_SEG_4_SLOPE       0x70
+#define DS2784_REG_AE_SEG_3_SLOPE       0x71
+#define DS2784_REG_AE_SEG_2_SLOPE       0x72
+#define DS2784_REG_AE_SEG_1_SLOPE       0x73
+#define DS2784_REG_SE_SEG_4_SLOPE       0x74
+#define DS2784_REG_SE_SEG_3_SLOPE       0x75
+#define DS2784_REG_SE_SEG_2_SLOPE       0x76
+#define DS2784_REG_SE_SEG_1_SLOPE       0x77
+#define DS2784_REG_RSGAIN_MSB           0x78
+#define DS2784_REG_RSGAIN_LSB           0x79
+#define DS2784_REG_RSTC                 0x7A
+#define DS2784_REG_CURR_OFFSET_BIAS     0x7B
+#define DS2784_REG_TBP34                0x7C
+#define DS2784_REG_TBP23                0x7D
+#define DS2784_REG_TBP12                0x7E
+#define DS2784_REG_PROTECTOR_THRESHOLD  0x7F
+#define DS2784_REG_USER_EEPROM_20       0x20
+
+#define DS2784_READ_DATA		0x69
+#define DS2784_WRITE_DATA		0x6C
+
+#define DS2784_DATA_SIZE		0xB2
+
+extern int w1_ds2784_read(struct device *dev, char *buf, int addr,
+			  size_t count);
+#endif /* !_W1_DS2784_H */
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
index 874aeb0..2aee2ac 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -38,6 +38,7 @@
 #define W1_EEPROM_DS2431	0x2D
 #define W1_FAMILY_DS2760	0x30
 #define W1_FAMILY_DS2780	0x32
+#define W1_FAMILY_DS2784	0x32
 #define W1_FAMILY_DS2781	0x3D
 #define W1_THERM_DS28EA00	0x42
 
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index 8558da9..bf9ca4e 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -69,6 +69,19 @@
 }
 
 /*
+ *	touch_hw_watchdog: ping the current watchdog device.
+ *
+ *	If there is no watchdog device, the nothing happens.
+ *	Otherwise we try to ping the watchdog.
+ */
+void touch_hw_watchdog(void)
+{
+	if (wdd)
+		watchdog_ping(wdd);
+}
+EXPORT_SYMBOL_GPL(touch_hw_watchdog);
+
+/*
  *	watchdog_start: wrapper to start the watchdog.
  *	@wddev: the watchdog device to start
  *
diff --git a/fs/exec.c b/fs/exec.c
index e3a7e36d..b2ae41a 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1261,6 +1261,13 @@
 			bprm->unsafe |= LSM_UNSAFE_PTRACE;
 	}
 
+	/*
+	 * This isn't strictly necessary, but it makes it harder for LSMs to
+	 * mess up.
+	 */
+	if (current->no_new_privs)
+		bprm->unsafe |= LSM_UNSAFE_NO_NEW_PRIVS;
+
 	n_fs = 1;
 	spin_lock(&p->fs->lock);
 	rcu_read_lock();
@@ -1304,7 +1311,8 @@
 	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) &&
+	    !current->no_new_privs) {
 		/* Set-uid? */
 		if (mode & S_ISUID) {
 			bprm->per_clear |= PER_CLEAR_ON_SETID;
diff --git a/fs/open.c b/fs/open.c
index cf1d34f..70f6f3e 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -836,6 +836,8 @@
 static void __put_unused_fd(struct files_struct *files, unsigned int fd)
 {
 	struct fdtable *fdt = files_fdtable(files);
+	BUG_ON(fdt->fd[fd] != NULL);
+	smp_wmb();
 	__clear_open_fd(fd, fdt);
 	if (fd < files->next_fd)
 		files->next_fd = fd;
diff --git a/include/asm-generic/siginfo.h b/include/asm-generic/siginfo.h
index 5e5e386..8ed6777 100644
--- a/include/asm-generic/siginfo.h
+++ b/include/asm-generic/siginfo.h
@@ -98,9 +98,18 @@
 			__ARCH_SI_BAND_T _band;	/* POLL_IN, POLL_OUT, POLL_MSG */
 			int _fd;
 		} _sigpoll;
+
+		/* SIGSYS */
+		struct {
+			void __user *_call_addr; /* calling user insn */
+			int _syscall;	/* triggering system call number */
+			unsigned int _arch;	/* AUDIT_ARCH_* of syscall */
+		} _sigsys;
 	} _sifields;
 } __ARCH_SI_ATTRIBUTES siginfo_t;
 
+/* If the arch shares siginfo, then it has SIGSYS. */
+#define __ARCH_SIGSYS
 #endif
 
 /*
@@ -124,6 +133,11 @@
 #define si_addr_lsb	_sifields._sigfault._addr_lsb
 #define si_band		_sifields._sigpoll._band
 #define si_fd		_sifields._sigpoll._fd
+#ifdef __ARCH_SIGSYS
+#define si_call_addr	_sifields._sigsys._call_addr
+#define si_syscall	_sifields._sigsys._syscall
+#define si_arch		_sifields._sigsys._arch
+#endif
 
 #ifdef __KERNEL__
 #define __SI_MASK	0xffff0000u
@@ -134,6 +148,7 @@
 #define __SI_CHLD	(4 << 16)
 #define __SI_RT		(5 << 16)
 #define __SI_MESGQ	(6 << 16)
+#define __SI_SYS	(7 << 16)
 #define __SI_CODE(T,N)	((T) | ((N) & 0xffff))
 #else
 #define __SI_KILL	0
@@ -143,6 +158,7 @@
 #define __SI_CHLD	0
 #define __SI_RT		0
 #define __SI_MESGQ	0
+#define __SI_SYS	0
 #define __SI_CODE(T,N)	(N)
 #endif
 
@@ -240,6 +256,12 @@
 #define NSIGPOLL	6
 
 /*
+ * SIGSYS si_codes
+ */
+#define SYS_SECCOMP		(__SI_SYS|1)	/* seccomp triggered */
+#define NSIGSYS	1
+
+/*
  * sigevent definitions
  * 
  * It seems likely that SIGEV_THREAD will have to be handled from 
diff --git a/include/asm-generic/syscall.h b/include/asm-generic/syscall.h
index 5c122ae..5b09392 100644
--- a/include/asm-generic/syscall.h
+++ b/include/asm-generic/syscall.h
@@ -142,4 +142,18 @@
 			   unsigned int i, unsigned int n,
 			   const unsigned long *args);
 
+/**
+ * syscall_get_arch - return the AUDIT_ARCH for the current system call
+ * @task:	task of interest, must be in system call entry tracing
+ * @regs:	task_pt_regs() of @task
+ *
+ * Returns the AUDIT_ARCH_* based on the system call convention in use.
+ *
+ * It's only valid to call this when @task is stopped on entry to a system
+ * call, due to %TIF_SYSCALL_TRACE, %TIF_SYSCALL_AUDIT, or %TIF_SECCOMP.
+ *
+ * Architectures which permit CONFIG_HAVE_ARCH_SECCOMP_FILTER must
+ * provide an implementation of this.
+ */
+int syscall_get_arch(struct task_struct *task, struct pt_regs *regs);
 #endif	/* _ASM_SYSCALL_H */
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 4bf4100..d984dac 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -336,6 +336,7 @@
 header-y += sched.h
 header-y += screen_info.h
 header-y += sdla.h
+header-y += seccomp.h
 header-y += securebits.h
 header-y += selinux_netlink.h
 header-y += sem.h
diff --git a/include/linux/alarmtimer.h b/include/linux/alarmtimer.h
index 96c5c24..f122c9f 100644
--- a/include/linux/alarmtimer.h
+++ b/include/linux/alarmtimer.h
@@ -35,6 +35,7 @@
  */
 struct alarm {
 	struct timerqueue_node	node;
+	struct hrtimer		timer;
 	enum alarmtimer_restart	(*function)(struct alarm *, ktime_t now);
 	enum alarmtimer_type	type;
 	int			state;
@@ -43,7 +44,7 @@
 
 void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
 		enum alarmtimer_restart (*function)(struct alarm *, ktime_t));
-void alarm_start(struct alarm *alarm, ktime_t start);
+int alarm_start(struct alarm *alarm, ktime_t start);
 int alarm_try_to_cancel(struct alarm *alarm);
 int alarm_cancel(struct alarm *alarm);
 
diff --git a/include/linux/audit.h b/include/linux/audit.h
index ed3ef19..22f292a 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -463,7 +463,7 @@
 extern void __audit_inode(const char *name, const struct dentry *dentry);
 extern void __audit_inode_child(const struct dentry *dentry,
 				const struct inode *parent);
-extern void __audit_seccomp(unsigned long syscall);
+extern void __audit_seccomp(unsigned long syscall, long signr, int code);
 extern void __audit_ptrace(struct task_struct *t);
 
 static inline int audit_dummy_context(void)
@@ -508,10 +508,10 @@
 }
 void audit_core_dumps(long signr);
 
-static inline void audit_seccomp(unsigned long syscall)
+static inline void audit_seccomp(unsigned long syscall, long signr, int code)
 {
 	if (unlikely(!audit_dummy_context()))
-		__audit_seccomp(syscall);
+		__audit_seccomp(syscall, signr, code);
 }
 
 static inline void audit_ptrace(struct task_struct *t)
@@ -634,7 +634,7 @@
 #define audit_inode(n,d) do { (void)(d); } while (0)
 #define audit_inode_child(i,p) do { ; } while (0)
 #define audit_core_dumps(i) do { ; } while (0)
-#define audit_seccomp(i) do { ; } while (0)
+#define audit_seccomp(i,s,c) do { ; } while (0)
 #define auditsc_get_stamp(c,t,s) (0)
 #define audit_get_loginuid(t) (-1)
 #define audit_get_sessionid(t) (-1)
diff --git a/include/linux/fb.h b/include/linux/fb.h
index d31cb68..45e73aa 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -231,6 +231,10 @@
 #define FB_VMODE_SMOOTH_XPAN	512	/* smooth xpan possible (internally used) */
 #define FB_VMODE_CONUPDATE	512	/* don't update x/yoffset	*/
 
+#define FB_FLAG_RATIO_4_3	64
+#define FB_FLAG_RATIO_16_9	128
+#define FB_FLAG_PIXEL_REPEAT	256
+
 /*
  * Display rotation support
  */
@@ -444,6 +448,8 @@
 
 #define FB_MISC_PRIM_COLOR	1
 #define FB_MISC_1ST_DETAIL	2	/* First Detailed Timing is preferred */
+#define FB_MISC_HDMI		4	/* display supports HDMI signaling */
+
 struct fb_chroma {
 	__u32 redx;	/* in fraction of 1024 */
 	__u32 greenx;
@@ -483,6 +489,8 @@
 	__u8  revision;			/* ...and revision */
 	__u8  max_x;			/* Maximum horizontal size (cm) */
 	__u8  max_y;			/* Maximum vertical size (cm) */
+	struct fb_audio *audiodb;	/* audio database */
+	__u32 audiodb_len;		/* audio database length */
 };
 
 struct fb_cmap_user {
@@ -1110,6 +1118,7 @@
 
 /* drivers/video/modedb.c */
 #define VESA_MODEDB_SIZE 34
+#define CEA_MODEDB_SIZE 65
 extern void fb_var_to_videomode(struct fb_videomode *mode,
 				const struct fb_var_screeninfo *var);
 extern void fb_videomode_to_var(struct fb_var_screeninfo *var,
@@ -1160,9 +1169,30 @@
 	u32 flag;
 };
 
+#define FB_AUDIO_LPCM	1
+
+#define FB_AUDIO_192KHZ	(1 << 6)
+#define FB_AUDIO_176KHZ	(1 << 5)
+#define FB_AUDIO_96KHZ	(1 << 4)
+#define FB_AUDIO_88KHZ	(1 << 3)
+#define FB_AUDIO_48KHZ	(1 << 2)
+#define FB_AUDIO_44KHZ	(1 << 1)
+#define FB_AUDIO_32KHZ	(1 << 0)
+
+#define FB_AUDIO_24BIT	(1 << 2)
+#define FB_AUDIO_20BIT	(1 << 1)
+#define FB_AUDIO_16BIT	(1 << 0)
+
+struct fb_audio {
+	u8 format;
+	u8 channel_count;
+	u8 sample_rates;
+	u8 bit_rates;
+};
+
 extern const char *fb_mode_option;
 extern const struct fb_videomode vesa_modes[];
-extern const struct fb_videomode cea_modes[64];
+extern const struct fb_videomode cea_modes[];
 
 struct fb_modelist {
 	struct list_head list;
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 8eeb205..f2e5315 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -10,6 +10,7 @@
 
 #ifdef __KERNEL__
 #include <linux/atomic.h>
+#include <linux/compat.h>
 #endif
 
 /*
@@ -132,6 +133,16 @@
 
 #ifdef __KERNEL__
 
+#ifdef CONFIG_COMPAT
+/*
+ * A struct sock_filter is architecture independent.
+ */
+struct compat_sock_fprog {
+	u16		len;
+	compat_uptr_t	filter;		/* struct sock_filter * */
+};
+#endif
+
 struct sk_buff;
 struct sock;
 
@@ -228,6 +239,7 @@
 	BPF_S_ANC_HATYPE,
 	BPF_S_ANC_RXHASH,
 	BPF_S_ANC_CPU,
+	BPF_S_ANC_SECCOMP_LD_W,
 };
 
 #endif /* __KERNEL__ */
diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h
index f027f7a..47c5ac3 100644
--- a/include/linux/i2c/atmel_mxt_ts.h
+++ b/include/linux/i2c/atmel_mxt_ts.h
@@ -39,6 +39,10 @@
 	unsigned int voltage;
 	unsigned char orient;
 	unsigned long irqflags;
+	unsigned char boot_address;
+	const char *firmware_name;
+	unsigned int gpio_reset;
+	unsigned int reset_msec;
 };
 
 #endif /* __LINUX_ATMEL_MXT_TS_H */
diff --git a/include/linux/input.h b/include/linux/input.h
index 49fb20e..bdbf553 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -1310,7 +1310,9 @@
 	struct mutex mutex;
 
 	unsigned int users;
+	unsigned int users_private;
 	bool going_away;
+	bool disabled;
 
 	bool sync;
 
diff --git a/include/linux/keyreset.h b/include/linux/keyreset.h
index a2ac49e..22ebe11 100644
--- a/include/linux/keyreset.h
+++ b/include/linux/keyreset.h
@@ -21,6 +21,7 @@
 
 struct keyreset_platform_data {
 	int (*reset_fn)(void);
+	int down_time_ms;
 	int *keys_up;
 	int keys_down[]; /* 0 terminated */
 };
diff --git a/include/linux/leds-as3668.h b/include/linux/leds-as3668.h
new file mode 100644
index 0000000..1307bb5
--- /dev/null
+++ b/include/linux/leds-as3668.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics. All rights reserved.
+ *
+ * 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 _LINUX_AS3668_H
+#define _LINUX_AS3668_H
+
+#define AS3668_VMON_VBAT_2_0V			0x00
+#define AS3668_VMON_VBAT_3_0V			0x01
+#define AS3668_VMON_VBAT_3_15V			0x02
+#define AS3668_VMON_VBAT_3_3V			0x03
+
+#define AS3668_SHUTDOWN_ENABLE_ON		1
+#define AS3668_SHUTDOWN_ENABLE_OFF		0
+
+#define AS3668_PATTERN_START_SOURCE_SW		0
+#define AS3668_PATTERN_START_SOURCE_GPIO	1
+
+#define AS3668_PWM_SOURCE_INTERNAL		0
+#define AS3668_PWM_SOURCE_EXTERNAL		1
+
+#define AS3668_GPIO_INPUT_NONINVERT		0
+#define AS3668_GPIO_INPUT_INVERT		1
+
+#define AS3668_GPIO_INPUT_MODE_ANALOG		0
+#define AS3668_GPIO_INPUT_MODE_DIGITAL		1
+
+#define AS3668_GPIO_MODE_INPUT_ONLY		0
+#define AS3668_GPIO_MODE_OUTPUT			1
+
+#define AS3668_AUDIO_CTRL_INPUT_GPIO		0
+#define AS3668_AUDIO_CTRL_INPUT_CURR4		1
+
+#define AS3668_AUDIO_CTRL_PLDN_ENABLE		0
+#define AS3668_AUDIO_CTRL_PLDN_DISABLE		1
+
+#define AS3668_AUDIO_CTRL_ADC_CHAR_250		0
+#define AS3668_AUDIO_CTRL_ADC_CHAR_50		1
+
+#define AS3668_AUDIO_INPUT_CAP_PRECHARGE	0
+#define AS3668_AUDIO_INPUT_CAP_NO_PRECHARGE	1
+
+#define AS3668_AUDIO_INPUT_AUTO_PRECHARGE	0
+#define AS3668_AUDIO_INPUT_MANUAL_PRECHARGE	1
+
+#define AS3668_RED				24
+#define AS3668_GREEN				16
+#define AS3668_BLUE				8
+#define AS3668_WHITE				0
+
+#define AS3668_LED_NUM				4
+
+struct as3668_platform_data {
+	u8 led_array[AS3668_LED_NUM];
+	u16 vbat_monitor_voltage_index:2;
+	u16 shutdown_enable:1;
+	u16 pattern_start_source:1;
+	u16 pwm_source:1;
+	u16 gpio_input_invert:1;
+	u16 gpio_input_mode:1;
+	u16 gpio_mode:1;
+	u16 audio_input_pin:1;
+	u16 audio_pulldown_off:1;
+	u16 audio_adc_characteristic:1;
+	u16 audio_dis_start:1;
+	u16 audio_man_start:1;
+} __packed;
+#endif
diff --git a/include/linux/mfd/max77686-private.h b/include/linux/mfd/max77686-private.h
new file mode 100644
index 0000000..a63a1c6
--- /dev/null
+++ b/include/linux/mfd/max77686-private.h
@@ -0,0 +1,254 @@
+/*
+ * max77686.h - Voltage regulator driver for the Maxim 77686
+ *
+ *  Copyright (C) 2011 Samsung Electrnoics
+ *  Chiwoong Byun <woong.byun@samsung.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 __LINUX_MFD_MAX77686_PRIV_H
+#define __LINUX_MFD_MAX77686_PRIV_H
+
+#include <linux/i2c.h>
+
+#define MAX77686_REG_INVALID		(0xff)
+
+enum max77686_pmic_reg {
+	MAX77686_REG_DEVICE_ID		= 0x00,
+	MAX77686_REG_INTSRC		= 0x01,
+	MAX77686_REG_INT1		= 0x02,
+	MAX77686_REG_INT2		= 0x03,
+
+	MAX77686_REG_INT1MSK		= 0x04,
+	MAX77686_REG_INT2MSK		= 0x05,
+
+	MAX77686_REG_STATUS1		= 0x06,
+	MAX77686_REG_STATUS2		= 0x07,
+
+	MAX77686_REG_PWRON		= 0x08,
+	MAX77686_REG_ONOFF_DELAY	= 0x09,
+	MAX77686_REG_MRSTB		= 0x0A,
+	/* Reserved: 0x0B-0x0F */
+
+	MAX77686_REG_BUCK1CTRL		= 0x10,
+	MAX77686_REG_BUCK1OUT		= 0x11,
+	MAX77686_REG_BUCK2CTRL1		= 0x12,
+	MAX77686_REG_BUCK234FREQ	= 0x13,
+	MAX77686_REG_BUCK2DVS1		= 0x14,
+	MAX77686_REG_BUCK2DVS2		= 0x15,
+	MAX77686_REG_BUCK2DVS3		= 0x16,
+	MAX77686_REG_BUCK2DVS4		= 0x17,
+	MAX77686_REG_BUCK2DVS5		= 0x18,
+	MAX77686_REG_BUCK2DVS6		= 0x19,
+	MAX77686_REG_BUCK2DVS7		= 0x1A,
+	MAX77686_REG_BUCK2DVS8		= 0x1B,
+	MAX77686_REG_BUCK3CTRL1		= 0x1C,
+	/* Reserved: 0x1D */
+	MAX77686_REG_BUCK3DVS1		= 0x1E,
+	MAX77686_REG_BUCK3DVS2		= 0x1F,
+	MAX77686_REG_BUCK3DVS3		= 0x20,
+	MAX77686_REG_BUCK3DVS4		= 0x21,
+	MAX77686_REG_BUCK3DVS5		= 0x22,
+	MAX77686_REG_BUCK3DVS6		= 0x23,
+	MAX77686_REG_BUCK3DVS7		= 0x24,
+	MAX77686_REG_BUCK3DVS8		= 0x25,
+	MAX77686_REG_BUCK4CTRL1		= 0x26,
+	/* Reserved: 0x27 */
+	MAX77686_REG_BUCK4DVS1		= 0x28,
+	MAX77686_REG_BUCK4DVS2		= 0x29,
+	MAX77686_REG_BUCK4DVS3		= 0x2A,
+	MAX77686_REG_BUCK4DVS4		= 0x2B,
+	MAX77686_REG_BUCK4DVS5		= 0x2C,
+	MAX77686_REG_BUCK4DVS6		= 0x2D,
+	MAX77686_REG_BUCK4DVS7		= 0x2E,
+	MAX77686_REG_BUCK4DVS8		= 0x2F,
+	MAX77686_REG_BUCK5CTRL		= 0x30,
+	MAX77686_REG_BUCK5OUT		= 0x31,
+	MAX77686_REG_BUCK6CTRL		= 0x32,
+	MAX77686_REG_BUCK6OUT		= 0x33,
+	MAX77686_REG_BUCK7CTRL		= 0x34,
+	MAX77686_REG_BUCK7OUT		= 0x35,
+	MAX77686_REG_BUCK8CTRL		= 0x36,
+	MAX77686_REG_BUCK8OUT		= 0x37,
+	MAX77686_REG_BUCK9CTRL		= 0x38,
+	MAX77686_REG_BUCK9OUT		= 0x39,
+	/* Reserved: 0x3A-0x3F */
+
+	MAX77686_REG_LDO1CTRL1		= 0x40,
+	MAX77686_REG_LDO2CTRL1		= 0x41,
+	MAX77686_REG_LDO3CTRL1		= 0x42,
+	MAX77686_REG_LDO4CTRL1		= 0x43,
+	MAX77686_REG_LDO5CTRL1		= 0x44,
+	MAX77686_REG_LDO6CTRL1		= 0x45,
+	MAX77686_REG_LDO7CTRL1		= 0x46,
+	MAX77686_REG_LDO8CTRL1		= 0x47,
+	MAX77686_REG_LDO9CTRL1		= 0x48,
+	MAX77686_REG_LDO10CTRL1		= 0x49,
+	MAX77686_REG_LDO11CTRL1		= 0x4A,
+	MAX77686_REG_LDO12CTRL1		= 0x4B,
+	MAX77686_REG_LDO13CTRL1		= 0x4C,
+	MAX77686_REG_LDO14CTRL1		= 0x4D,
+	MAX77686_REG_LDO15CTRL1		= 0x4E,
+	MAX77686_REG_LDO16CTRL1		= 0x4F,
+	MAX77686_REG_LDO17CTRL1		= 0x50,
+	MAX77686_REG_LDO18CTRL1		= 0x51,
+	MAX77686_REG_LDO19CTRL1		= 0x52,
+	MAX77686_REG_LDO20CTRL1		= 0x53,
+	MAX77686_REG_LDO21CTRL1		= 0x54,
+	MAX77686_REG_LDO22CTRL1		= 0x55,
+	MAX77686_REG_LDO23CTRL1		= 0x56,
+	MAX77686_REG_LDO24CTRL1		= 0x57,
+	MAX77686_REG_LDO25CTRL1		= 0x58,
+	MAX77686_REG_LDO26CTRL1		= 0x59,
+	/* Reserved: 0x5A-0x5F */
+	MAX77686_REG_LDO1CTRL2		= 0x60,
+	MAX77686_REG_LDO2CTRL2		= 0x61,
+	MAX77686_REG_LDO3CTRL2		= 0x62,
+	MAX77686_REG_LDO4CTRL2		= 0x63,
+	MAX77686_REG_LDO5CTRL2		= 0x64,
+	MAX77686_REG_LDO6CTRL2		= 0x65,
+	MAX77686_REG_LDO7CTRL2		= 0x66,
+	MAX77686_REG_LDO8CTRL2		= 0x67,
+	MAX77686_REG_LDO9CTRL2		= 0x68,
+	MAX77686_REG_LDO10CTRL2		= 0x69,
+	MAX77686_REG_LDO11CTRL2		= 0x6A,
+	MAX77686_REG_LDO12CTRL2		= 0x6B,
+	MAX77686_REG_LDO13CTRL2		= 0x6C,
+	MAX77686_REG_LDO14CTRL2		= 0x6D,
+	MAX77686_REG_LDO15CTRL2		= 0x6E,
+	MAX77686_REG_LDO16CTRL2		= 0x6F,
+	MAX77686_REG_LDO17CTRL2		= 0x70,
+	MAX77686_REG_LDO18CTRL2		= 0x71,
+	MAX77686_REG_LDO19CTRL2		= 0x72,
+	MAX77686_REG_LDO20CTRL2		= 0x73,
+	MAX77686_REG_LDO21CTRL2		= 0x74,
+	MAX77686_REG_LDO22CTRL2		= 0x75,
+	MAX77686_REG_LDO23CTRL2		= 0x76,
+	MAX77686_REG_LDO24CTRL2		= 0x77,
+	MAX77686_REG_LDO25CTRL2		= 0x78,
+	MAX77686_REG_LDO26CTRL2		= 0x79,
+	/* Reserved: 0x7A-0x7D */
+
+	MAX77686_REG_BBAT_CHG		= 0x7E,
+	MAX77686_REG_32KHZ			= 0x7F,
+
+	MAX77686_REG_PMIC_END		= 0x80,
+};
+
+enum max77686_rtc_reg {
+	MAX77686_RTC_INT			= 0x00,
+	MAX77686_RTC_INTM			= 0x01,
+	MAX77686_RTC_CONTROLM		= 0x02,
+	MAX77686_RTC_CONTROL		= 0x03,
+	MAX77686_RTC_UPDATE0		= 0x04,
+	/* Reserved: 0x5 */
+	MAX77686_WTSR_SMPL_CNTL		= 0x06,
+	MAX77686_RTC_SEC			= 0x07,
+	MAX77686_RTC_MIN			= 0x08,
+	MAX77686_RTC_HOUR			= 0x09,
+	MAX77686_RTC_WEEKDAY		= 0x0A,
+	MAX77686_RTC_MONTH			= 0x0B,
+	MAX77686_RTC_YEAR			= 0x0C,
+	MAX77686_RTC_DATE			= 0x0D,
+	MAX77686_ALARM1_SEC			= 0x0E,
+	MAX77686_ALARM1_MIN			= 0x0F,
+	MAX77686_ALARM1_HOUR		= 0x10,
+	MAX77686_ALARM1_WEEKDAY		= 0x11,
+	MAX77686_ALARM1_MONTH		= 0x12,
+	MAX77686_ALARM1_YEAR		= 0x13,
+	MAX77686_ALARM1_DATE		= 0x14,
+	MAX77686_ALARM2_SEC			= 0x15,
+	MAX77686_ALARM2_MIN			= 0x16,
+	MAX77686_ALARM2_HOUR		= 0x17,
+	MAX77686_ALARM2_WEEKDAY		= 0x18,
+	MAX77686_ALARM2_MONTH		= 0x19,
+	MAX77686_ALARM2_YEAR		= 0x1A,
+	MAX77686_ALARM2_DATE		= 0x1B,
+};
+
+#define MAX77686_IRQSRC_PMIC	(0)
+#define MAX77686_IRQSRC_RTC		(1 << 0)
+
+#define MAX77686_REG_RAMP_RATE_100MV	(0x3<<6)
+#define MAX77686_REG_RAMP_RATE_55MV		(0x2<<6)
+#define MAX77686_REG_RAMP_RATE_27MV		(0x1<<6)
+#define MAX77686_REG_RAMP_RATE_13MV		(0x0<<6)
+
+enum max77686_irq_source {
+	PMIC_INT1 = 0,
+	PMIC_INT2,
+	RTC_INT,
+
+	MAX77686_IRQ_GROUP_NR,
+};
+
+enum max77686_irq {
+	MAX77686_PMICIRQ_PWRONF,
+	MAX77686_PMICIRQ_PWRONR,
+	MAX77686_PMICIRQ_JIGONBF,
+	MAX77686_PMICIRQ_JIGONBR,
+	MAX77686_PMICIRQ_ACOKBF,
+	MAX77686_PMICIRQ_ACOKBR,
+	MAX77686_PMICIRQ_ONKEY1S,
+	MAX77686_PMICIRQ_MRSTB,
+
+	MAX77686_PMICIRQ_140C,
+	MAX77686_PMICIRQ_120C,
+
+	MAX77686_RTCIRQ_RTC60S,
+	MAX77686_RTCIRQ_RTCA1,
+	MAX77686_RTCIRQ_RTCA2,
+	MAX77686_RTCIRQ_SMPL,
+	MAX77686_RTCIRQ_RTC1S,
+	MAX77686_RTCIRQ_WTSR,
+
+	MAX77686_IRQ_NR,
+};
+
+struct max77686_dev {
+	struct device *dev;
+	struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */
+	struct i2c_client *rtc; /* slave addr 0x0c */
+	struct mutex iolock;
+
+	int type;
+
+	int irq;
+	int irq_gpio;
+	int irq_base;
+	bool wakeup;
+	struct mutex irqlock;
+	int irq_masks_cur[MAX77686_IRQ_GROUP_NR];
+	int irq_masks_cache[MAX77686_IRQ_GROUP_NR];
+};
+
+enum max77686_types {
+	TYPE_MAX77686,
+};
+
+extern int max77686_irq_init(struct max77686_dev *max77686);
+extern void max77686_irq_exit(struct max77686_dev *max77686);
+extern int max77686_irq_resume(struct max77686_dev *max77686);
+
+extern int max77686_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest);
+extern int max77686_bulk_read(struct i2c_client *i2c, u8 reg, int count,
+				u8 *buf);
+extern int max77686_write_reg(struct i2c_client *i2c, u8 reg, u8 value);
+extern int max77686_bulk_write(struct i2c_client *i2c, u8 reg, int count,
+				u8 *buf);
+extern int max77686_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask);
+
+#endif /*  __LINUX_MFD_MAX77686_PRIV_H */
diff --git a/include/linux/mfd/max77686.h b/include/linux/mfd/max77686.h
new file mode 100644
index 0000000..5e36917
--- /dev/null
+++ b/include/linux/mfd/max77686.h
@@ -0,0 +1,151 @@
+/*
+ * max77686.h - Driver for the Maxim 77686
+ *
+ *  Copyright (C) 2011 Samsung Electrnoics
+ *  Chiwoong Byun <woong.byun@samsung.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
+ *
+ * This driver is based on max8997.h
+ *
+ * MAX77686 has PMIC, RTC devices.
+ * The devices share the same I2C bus and included in
+ * this mfd driver.
+ */
+
+#ifndef __LINUX_MFD_MAX77686_H
+#define __LINUX_MFD_MAX77686_H
+
+#include <linux/regulator/consumer.h>
+#include <linux/rtc.h>
+
+/* MAX77686 regulator IDs */
+enum max77686_regulators {
+	MAX77686_LDO1 = 0,
+	MAX77686_LDO2,
+	MAX77686_LDO3,
+	MAX77686_LDO4,
+	MAX77686_LDO5,
+	MAX77686_LDO6,
+	MAX77686_LDO7,
+	MAX77686_LDO8,
+	MAX77686_LDO9,
+	MAX77686_LDO10,
+	MAX77686_LDO11,
+	MAX77686_LDO12,
+	MAX77686_LDO13,
+	MAX77686_LDO14,
+	MAX77686_LDO15,
+	MAX77686_LDO16,
+	MAX77686_LDO17,
+	MAX77686_LDO18,
+	MAX77686_LDO19,
+	MAX77686_LDO20,
+	MAX77686_LDO21,
+	MAX77686_LDO22,
+	MAX77686_LDO23,
+	MAX77686_LDO24,
+	MAX77686_LDO25,
+	MAX77686_LDO26,
+	MAX77686_BUCK1,
+	MAX77686_BUCK2,
+	MAX77686_BUCK3,
+	MAX77686_BUCK4,
+	MAX77686_BUCK5,
+	MAX77686_BUCK6,
+	MAX77686_BUCK7,
+	MAX77686_BUCK8,
+	MAX77686_BUCK9,
+	MAX77686_EN32KHZ_AP,
+	MAX77686_EN32KHZ_CP,
+	MAX77686_P32KH,
+
+	MAX77686_REG_MAX,
+};
+
+struct max77686_regulator_data {
+	int id;
+	struct regulator_init_data *initdata;
+};
+
+enum max77686_opmode {
+	MAX77686_OPMODE_NORMAL,
+	MAX77686_OPMODE_LP,
+	MAX77686_OPMODE_STANDBY,
+};
+
+enum max77686_ramp_rate {
+	MAX77686_RAMP_RATE_100MV,
+	MAX77686_RAMP_RATE_13MV,
+	MAX77686_RAMP_RATE_27MV,
+	MAX77686_RAMP_RATE_55MV,
+};
+
+struct max77686_opmode_data {
+	int id;
+	int mode;
+};
+
+/**
+ * struct max77686_wtsr_smpl - settings for WTSR/SMPL
+ * @wtsr_en:		WTSR Function Enable Control
+ * @smpl_en:		SMPL Function Enable Control
+ * @wtsr_timer_val:	Set the WTSR timer Threshold
+ *			0(250ms), 1(500ms), 2(750ms), 3(1000ms)
+ * @smpl_timer_val:	Set the SMPL timer Threshold
+ *			0(0.5s), 1(1.0s), 2(1.5s), 3(2.0s)
+ * @check_jigon:	if this value is true, do not enable SMPL function when
+ *			JIGONB is low(JIG cable is attached)
+ */
+struct max77686_wtsr_smpl {
+	bool wtsr_en;
+	bool smpl_en;
+	int wtsr_timer_val;
+	int smpl_timer_val;
+	bool check_jigon;
+};
+
+struct max77686_platform_data {
+	/* IRQ */
+	int irq_gpio;
+	int irq_base;
+	int ono;
+	int wakeup;
+
+	/* ---- PMIC ---- */
+	struct max77686_regulator_data *regulators;
+	int num_regulators;
+	int has_full_constraints;
+
+	struct max77686_opmode_data *opmode_data;
+	int ramp_rate;
+
+	/*
+	 * GPIO-DVS feature is not enabled with the current version of
+	 * MAX77686 driver. Buck2/3/4_voltages[0] is used as the default
+	 * voltage at probe. DVS/SELB gpios are set as OUTPUT-LOW.
+	 */
+	int buck234_gpio_dvs[3]; /* GPIO of [0]DVS1, [1]DVS2, [2]DVS3 */
+	int buck234_gpio_selb[3]; /* [0]SELB2, [1]SELB3, [2]SELB4 */
+	unsigned int buck2_voltage[8]; /* buckx_voltage in uV */
+	unsigned int buck3_voltage[8];
+	unsigned int buck4_voltage[8];
+
+	/* ---- RTC ---- */
+	struct max77686_wtsr_smpl *wtsr_smpl;
+	struct rtc_time *init_time;
+};
+
+#endif /* __LINUX_MFD_MAX77686_H */
diff --git a/include/linux/mfd/wm8994/core.h b/include/linux/mfd/wm8994/core.h
index 9eff2a3..d41bc7b 100644
--- a/include/linux/mfd/wm8994/core.h
+++ b/include/linux/mfd/wm8994/core.h
@@ -57,6 +57,7 @@
 
 	enum wm8994_type type;
 	int revision;
+	int cust_id;
 
 	struct device *dev;
 	struct regmap *regmap;
diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h
index 893267b..681b912 100644
--- a/include/linux/mfd/wm8994/pdata.h
+++ b/include/linux/mfd/wm8994/pdata.h
@@ -17,6 +17,7 @@
 
 #define WM8994_NUM_LDO   2
 #define WM8994_NUM_GPIO 11
+#define WM8994_NUM_AIF   3
 
 struct wm8994_ldo_pdata {
 	/** GPIOs to enable regulator, 0 or less if not available */
@@ -28,7 +29,7 @@
 #define WM8994_CONFIGURE_GPIO 0x10000
 
 #define WM8994_DRC_REGS 5
-#define WM8994_EQ_REGS  20
+#define WM8994_EQ_REGS  21
 #define WM8958_MBC_CUTOFF_REGS 20
 #define WM8958_MBC_COEFF_REGS  48
 #define WM8958_MBC_COMBINED_REGS 56
@@ -141,6 +142,7 @@
 	struct wm8994_ldo_pdata ldo[WM8994_NUM_LDO];
 
 	int irq_base;  /** Base IRQ number for WM8994, required for IRQs */
+	unsigned long irq_flags; /** user irq flags */
 
         int num_drc_cfgs;
         struct wm8994_drc_cfg *drc_cfgs;
@@ -171,6 +173,11 @@
         unsigned int lineout1fb:1;
         unsigned int lineout2fb:1;
 
+	/* Delay between detecting a jack and starting microphone
+	 * detect (specified in ms)
+	 */
+	int micdet_delay;
+
 	/* IRQ for microphone detection if brought out directly as a
 	 * signal.
 	 */
@@ -205,6 +212,13 @@
 	 * system.
 	 */
 	bool spkmode_pu;
+
+	/**
+	 * Maximum number of channels clocks will be generated for,
+	 * useful for systems where and I2S bus with multiple data
+	 * lines is mastered.
+	 */
+	int max_channels_clocked[WM8994_NUM_AIF];
 };
 
 #endif
diff --git a/include/linux/mfd/wm8994/registers.h b/include/linux/mfd/wm8994/registers.h
index 86e6a03..0535489 100644
--- a/include/linux/mfd/wm8994/registers.h
+++ b/include/linux/mfd/wm8994/registers.h
@@ -2212,6 +2212,9 @@
 /*
  * R256 (0x100) - Chip Revision
  */
+#define WM8994_CUST_ID_MASK                     0xFF00  /* CUST_ID - [15:8] */
+#define WM8994_CUST_ID_SHIFT                         8  /* CUST_ID - [15:8] */
+#define WM8994_CUST_ID_WIDTH                         8  /* CUST_ID - [15:8] */
 #define WM8994_CHIP_REV_MASK                    0x000F  /* CHIP_REV - [3:0] */
 #define WM8994_CHIP_REV_SHIFT                        0  /* CHIP_REV - [3:0] */
 #define WM8994_CHIP_REV_WIDTH                        4  /* CHIP_REV - [3:0] */
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index de65861..2bd1769 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -53,9 +53,6 @@
 	u8			part_config;
 	u8			cache_ctrl;
 	u8			rst_n_function;
-	u8			max_packed_writes;
-	u8			max_packed_reads;
-	u8			packed_event_en;
 	unsigned int		part_time;		/* Units: ms */
 	unsigned int		sa_timeout;		/* Units: 100ns */
 	unsigned int		generic_cmd6_time;	/* Units: 10ms */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index d787037..1b431c7 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -18,9 +18,6 @@
 struct mmc_command {
 	u32			opcode;
 	u32			arg;
-#define MMC_CMD23_ARG_REL_WR	(1 << 31)
-#define MMC_CMD23_ARG_PACKED	((0 << 31) | (1 << 30))
-#define MMC_CMD23_ARG_TAG_REQ	(1 << 29)
 	u32			resp[4];
 	unsigned int		flags;		/* expected response type */
 #define MMC_RSP_PRESENT	(1 << 0)
@@ -146,7 +143,6 @@
 extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
 	struct mmc_command *, int);
 extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
-extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
 
 #define MMC_ERASE_ARG		0x00000000
 #define MMC_SECURE_ERASE_ARG	0x80000000
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 168148d..5674504 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -144,8 +144,6 @@
 struct mmc_async_req {
 	/* active mmc request */
 	struct mmc_request	*mrq;
-	struct mmc_request      *__mrq;
-	bool                    __cond;
 	/*
 	 * Check error status of completed mmc request.
 	 * Returns 0 if success otherwise non zero.
@@ -241,10 +239,6 @@
 #define MMC_CAP2_BROKEN_VOLTAGE	(1 << 7)	/* Use the broken voltage */
 #define MMC_CAP2_DETECT_ON_ERR	(1 << 8)	/* On I/O err check card removal */
 #define MMC_CAP2_HC_ERASE_SZ	(1 << 9)	/* High-capacity erase size */
-#define MMC_CAP2_PACKED_RD	(1 << 10)	/* Allow packed read */
-#define MMC_CAP2_PACKED_WR	(1 << 11)	/* Allow packed write */
-#define MMC_CAP2_PACKED_CMD	(MMC_CAP2_PACKED_RD | \
-				 MMC_CAP2_PACKED_WR) /* Allow packed commands */
 
 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
 	unsigned int        power_notify_type;
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 254901a..d425cab 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -139,7 +139,6 @@
 #define R1_CURRENT_STATE(x)	((x & 0x00001E00) >> 9)	/* sx, b (4 bits) */
 #define R1_READY_FOR_DATA	(1 << 8)	/* sx, a */
 #define R1_SWITCH_ERROR		(1 << 7)	/* sx, c */
-#define R1_EXP_EVENT		(1 << 6)	/* sr, a */
 #define R1_APP_CMD		(1 << 5)	/* sr, c */
 
 #define R1_STATE_IDLE	0
@@ -275,10 +274,6 @@
 #define EXT_CSD_FLUSH_CACHE		32      /* W */
 #define EXT_CSD_CACHE_CTRL		33      /* R/W */
 #define EXT_CSD_POWER_OFF_NOTIFICATION	34	/* R/W */
-#define EXT_CSD_PACKED_FAILURE_INDEX	35	/* RO */
-#define EXT_CSD_PACKED_CMD_STATUS	36	/* RO */
-#define EXT_CSD_EXP_EVENTS_STATUS	54	/* RO, 2 bytes */
-#define EXT_CSD_EXP_EVENTS_CTRL	56	/* R/W, 2 bytes */
 #define EXT_CSD_DATA_SECTOR_SIZE	61	/* R */
 #define EXT_CSD_GP_SIZE_MULT		143	/* R/W */
 #define EXT_CSD_PARTITION_ATTRIBUTE	156	/* R/W */
@@ -323,8 +318,6 @@
 #define EXT_CSD_CACHE_SIZE		249	/* RO, 4 bytes */
 #define EXT_CSD_TAG_UNIT_SIZE		498	/* RO */
 #define EXT_CSD_DATA_TAG_SUPPORT	499	/* RO */
-#define EXT_CSD_MAX_PACKED_WRITES	500	/* RO */
-#define EXT_CSD_MAX_PACKED_READS	501	/* RO */
 #define EXT_CSD_HPI_FEATURES		503	/* RO */
 
 /*
@@ -384,14 +377,6 @@
 #define EXT_CSD_PWR_CL_4BIT_MASK	0x0F	/* 8 bit PWR CLS */
 #define EXT_CSD_PWR_CL_8BIT_SHIFT	4
 #define EXT_CSD_PWR_CL_4BIT_SHIFT	0
-
-#define EXT_CSD_PACKED_EVENT_EN	(1 << 3)
-
-#define EXT_CSD_PACKED_FAILURE	(1 << 3)
-
-#define EXT_CSD_PACKED_GENERIC_ERROR	(1 << 0)
-#define EXT_CSD_PACKED_INDEXED_ERROR	(1 << 1)
-
 /*
  * MMC_SWITCH access modes
  */
diff --git a/include/linux/mpu.h b/include/linux/mpu.h
new file mode 100644
index 0000000..4391226
--- /dev/null
+++ b/include/linux/mpu.h
@@ -0,0 +1,108 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    mpu.h
+ *      @brief   mpu definition
+ */
+
+#ifndef __MPU_H_
+#define __MPU_H_
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#endif
+
+enum secondary_slave_type {
+	SECONDARY_SLAVE_TYPE_NONE,
+	SECONDARY_SLAVE_TYPE_ACCEL,
+	SECONDARY_SLAVE_TYPE_COMPASS,
+	SECONDARY_SLAVE_TYPE_PRESSURE,
+
+	SECONDARY_SLAVE_TYPE_TYPES
+};
+
+enum ext_slave_id {
+	ID_INVALID = 0,
+	GYRO_ID_MPU3050,
+	GYRO_ID_MPU6050A2,
+	GYRO_ID_MPU6050B1,
+	GYRO_ID_MPU6050B1_NO_ACCEL,
+	GYRO_ID_ITG3500,
+
+	ACCEL_ID_LIS331,
+	ACCEL_ID_LSM303DLX,
+	ACCEL_ID_LIS3DH,
+	ACCEL_ID_KXSD9,
+	ACCEL_ID_KXTF9,
+	ACCEL_ID_BMA150,
+	ACCEL_ID_BMA222,
+	ACCEL_ID_BMA250,
+	ACCEL_ID_ADXL34X,
+	ACCEL_ID_MMA8450,
+	ACCEL_ID_MMA845X,
+	ACCEL_ID_MPU6050,
+
+	COMPASS_ID_AK8963,
+	COMPASS_ID_AK8975,
+	COMPASS_ID_AK8972,
+	COMPASS_ID_AMI30X,
+	COMPASS_ID_AMI306,
+	COMPASS_ID_YAS529,
+	COMPASS_ID_YAS530,
+	COMPASS_ID_HMC5883,
+	COMPASS_ID_LSM303DLH,
+	COMPASS_ID_LSM303DLM,
+	COMPASS_ID_MMC314X,
+	COMPASS_ID_HSCDTD002B,
+	COMPASS_ID_HSCDTD004A,
+
+	PRESSURE_ID_BMA085,
+};
+
+#define INV_PROD_KEY(ver, rev) (ver * 100 + rev)
+/**
+ * struct mpu_platform_data - Platform data for the mpu driver
+ * @int_config:		Bits [7:3] of the int config register.
+ * @level_shifter:	0: VLogic, 1: VDD
+ * @orientation:	Orientation matrix of the gyroscope
+ * @sec_slave_type:     secondary slave device type, can be compass, accel, etc
+ * @sec_slave_id:       id of the secondary slave device
+ * @secondary_i2c_address: secondary device's i2c address
+ * @secondary_orientation: secondary device's orientation matrix
+ * @key:                key for MPL library.
+ *
+ * Contains platform specific information on how to configure the MPU3050 to
+ * work on this platform.  The orientation matricies are 3x3 rotation matricies
+ * that are applied to the data to rotate from the mounting orientation to the
+ * platform orientation.  The values must be one of 0, 1, or -1 and each row and
+ * column should have exactly 1 non-zero value.
+ */
+struct mpu_platform_data {
+	__u8 int_config;
+	__u8 level_shifter;
+	__s8 orientation[9];
+	enum secondary_slave_type sec_slave_type;
+	enum ext_slave_id sec_slave_id;
+	__u16 secondary_i2c_addr;
+	__s8 secondary_orientation[9];
+	__u8 key[16];
+};
+
+#endif	/* __MPU_H_ */
diff --git a/include/linux/nfc/bcm2079x.h b/include/linux/nfc/bcm2079x.h
new file mode 100644
index 0000000..c1349ca
--- /dev/null
+++ b/include/linux/nfc/bcm2079x.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 Broadcom Corporation.
+ *
+ * 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 _BCM2079X_H
+#define _BCM2079X_H
+
+#define BCMNFC_MAGIC	0xFA
+
+/*
+ * BCMNFC power control via ioctl
+ * BCMNFC_POWER_CTL(0): power off
+ * BCMNFC_POWER_CTL(1): power on
+ * BCMNFC_WAKE_CTL(0): wake off
+ * BCMNFC_WAKE_CTL(1): wake on
+ */
+#define BCMNFC_POWER_CTL		_IO(BCMNFC_MAGIC, 0x01)
+#define BCMNFC_CHANGE_ADDR		_IO(BCMNFC_MAGIC, 0x02)
+#define BCMNFC_READ_FULL_PACKET		_IO(BCMNFC_MAGIC, 0x03)
+#define BCMNFC_SET_WAKE_ACTIVE_STATE	_IO(BCMNFC_MAGIC, 0x04)
+#define BCMNFC_WAKE_CTL			_IO(BCMNFC_MAGIC, 0x05)
+#define BCMNFC_READ_MULTI_PACKETS	_IO(BCMNFC_MAGIC, 0x06)
+
+struct bcm2079x_platform_data {
+	unsigned int irq_gpio;
+	unsigned int en_gpio;
+	unsigned int wake_gpio;
+};
+
+#endif
diff --git a/include/linux/platform_data/bh1721fvc.h b/include/linux/platform_data/bh1721fvc.h
new file mode 100644
index 0000000..9d49068
--- /dev/null
+++ b/include/linux/platform_data/bh1721fvc.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics. All rights reserved.
+ *
+ * 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 _LINUX_BH1721FVC_H
+#define _LINUX_BH1721FVC_H
+
+struct bh1721fvc_platform_data {
+	int reset_pin;
+};
+
+#endif
diff --git a/include/linux/platform_data/es305.h b/include/linux/platform_data/es305.h
new file mode 100644
index 0000000..2f27505
--- /dev/null
+++ b/include/linux/platform_data/es305.h
@@ -0,0 +1,31 @@
+/*
+ * include/linux/platform_data/es305.h - Audience ES305 Voice Processor driver
+ *
+ * Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2012 Samsung Corporation.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __ES305_H__
+#define __ES305_H__
+
+struct es305_platform_data {
+	int gpio_wakeup;
+	int gpio_reset;
+
+	void (*clk_enable)(bool enable);
+
+	/* PORT A = 1, B = 2, C = 3, D = 4 */
+	int passthrough_src;
+	int passthrough_dst;
+};
+
+#endif
diff --git a/include/linux/platform_data/haptic_isa1200.h b/include/linux/platform_data/haptic_isa1200.h
new file mode 100644
index 0000000..cdc07c5
--- /dev/null
+++ b/include/linux/platform_data/haptic_isa1200.h
@@ -0,0 +1,26 @@
+/*
+ *  haptic_isa1200.h - ISA1200 Haptic Motor driver
+ *
+ * Copyright (C) 2012 Samsung Electronics Co. Ltd. All Rights Reserved.
+ * Author: Vishnudev Ramakrishnan <vramakri@sta.samsung.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _LINUX_HAPTIC_ISA1200_H
+#define _LINUX_HAPTIC_ISA1200_H
+
+struct isa1200_platform_data {
+	int pwm_ch;
+	int hap_en_gpio;
+	int max_timeout;
+};
+
+#endif /* _LINUX_HAPTIC_ISA1200_H */
diff --git a/include/linux/platform_data/stmpe811-adc.h b/include/linux/platform_data/stmpe811-adc.h
new file mode 100644
index 0000000..69eaf5c
--- /dev/null
+++ b/include/linux/platform_data/stmpe811-adc.h
@@ -0,0 +1,29 @@
+/*
+ * stmpe811-adc.h
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * SangYoung Son <hello.son@samsung.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 and
+ * only 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.
+ *
+ */
+
+#ifndef __STMPE811_ADC_H_
+#define __STMPE811_ADC_H_
+
+struct stmpe811_callbacks {
+	int (*get_adc_data)(u8 channel);
+};
+
+struct stmpe811_platform_data {
+	void (*register_cb)(struct stmpe811_callbacks *);
+};
+
+#endif
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index e1f5447..5a8e570 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -128,6 +128,10 @@
 	POWER_SUPPLY_PROP_USB_HC,
 	POWER_SUPPLY_PROP_USB_OTG,
 	POWER_SUPPLY_PROP_CHARGE_ENABLED,
+	POWER_SUPPLY_PROP_USB_INPRIORITY,
+	POWER_SUPPLY_PROP_AUTO_CURRENT_LIMIT,
+	POWER_SUPPLY_PROP_REMOTE_TYPE,
+	POWER_SUPPLY_PROP_CHARGER_DETECTION,
 	/* Properties of type `const char *' */
 	POWER_SUPPLY_PROP_MODEL_NAME,
 	POWER_SUPPLY_PROP_MANUFACTURER,
diff --git a/include/linux/prctl.h b/include/linux/prctl.h
index e0cfec2..78b76e2 100644
--- a/include/linux/prctl.h
+++ b/include/linux/prctl.h
@@ -124,4 +124,19 @@
 #define PR_SET_CHILD_SUBREAPER 36
 #define PR_GET_CHILD_SUBREAPER 37
 
+/*
+ * If no_new_privs is set, then operations that grant new privileges (i.e.
+ * execve) will either fail or not grant them.  This affects suid/sgid,
+ * file capabilities, and LSMs.
+ *
+ * Operations that merely manipulate or drop existing privileges (setresuid,
+ * capset, etc.) will still work.  Drop those privileges if you want them gone.
+ *
+ * Changing LSM security domain is considered a new privilege.  So, for example,
+ * asking selinux for a specific new context (e.g. with runcon) will result
+ * in execve returning -EPERM.
+ */
+#define PR_SET_NO_NEW_PRIVS 38
+#define PR_GET_NO_NEW_PRIVS 39
+
 #endif /* _LINUX_PRCTL_H */
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index 5c719627..597e4fd 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -58,6 +58,7 @@
 #define PTRACE_EVENT_EXEC	4
 #define PTRACE_EVENT_VFORK_DONE	5
 #define PTRACE_EVENT_EXIT	6
+#define PTRACE_EVENT_SECCOMP	7
 /* Extended result codes which enabled by means other than options.  */
 #define PTRACE_EVENT_STOP	128
 
@@ -69,8 +70,9 @@
 #define PTRACE_O_TRACEEXEC	(1 << PTRACE_EVENT_EXEC)
 #define PTRACE_O_TRACEVFORKDONE	(1 << PTRACE_EVENT_VFORK_DONE)
 #define PTRACE_O_TRACEEXIT	(1 << PTRACE_EVENT_EXIT)
+#define PTRACE_O_TRACESECCOMP	(1 << PTRACE_EVENT_SECCOMP)
 
-#define PTRACE_O_MASK		0x0000007f
+#define PTRACE_O_MASK		0x000000ff
 
 #include <asm/ptrace.h>
 
@@ -98,6 +100,7 @@
 #define PT_TRACE_EXEC		PT_EVENT_FLAG(PTRACE_EVENT_EXEC)
 #define PT_TRACE_VFORK_DONE	PT_EVENT_FLAG(PTRACE_EVENT_VFORK_DONE)
 #define PT_TRACE_EXIT		PT_EVENT_FLAG(PTRACE_EVENT_EXIT)
+#define PT_TRACE_SECCOMP	PT_EVENT_FLAG(PTRACE_EVENT_SECCOMP)
 
 /* single stepping state bits (used on ARM and PA-RISC) */
 #define PT_SINGLESTEP_BIT	31
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 937ab61..71df39a 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1344,6 +1344,8 @@
 				 * execve */
 	unsigned in_iowait:1;
 
+	/* task may not gain privileges */
+	unsigned no_new_privs:1;
 
 	/* Revert to default priority/policy when forking */
 	unsigned sched_reset_on_fork:1;
@@ -1453,7 +1455,7 @@
 	uid_t loginuid;
 	unsigned int sessionid;
 #endif
-	seccomp_t seccomp;
+	struct seccomp seccomp;
 
 /* Thread group tracking */
    	u32 parent_exec_id;
diff --git a/include/linux/sec_jack.h b/include/linux/sec_jack.h
new file mode 100755
index 0000000..d8182e3
--- /dev/null
+++ b/include/linux/sec_jack.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 Samsung Electronics, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ASM_ARCH_SEC_HEADSET_H
+#define __ASM_ARCH_SEC_HEADSET_H
+
+#ifdef __KERNEL__
+
+enum {
+	SEC_JACK_NO_DEVICE		= 0x0,
+	SEC_HEADSET_4POLE		= 0x01 << 0,
+	SEC_HEADSET_3POLE		= 0x01 << 1,
+	SEC_TTY_DEVICE			= 0x01 << 2,
+	SEC_FM_HEADSET			= 0x01 << 3,
+	SEC_FM_SPEAKER			= 0x01 << 4,
+	SEC_TVOUT_DEVICE		= 0x01 << 5,
+	SEC_EXTRA_DOCK_SPEAKER		= 0x01 << 6,
+	SEC_EXTRA_CAR_DOCK_SPEAKER	= 0x01 << 7,
+	SEC_UNKNOWN_DEVICE		= 0x01 << 8,
+};
+
+struct sec_jack_zone {
+	unsigned int adc_high;
+	unsigned int delay_ms;
+	unsigned int check_count;
+	unsigned int jack_type;
+};
+
+struct sec_jack_buttons_zone {
+	unsigned int code;
+	unsigned int adc_low;
+	unsigned int adc_high;
+};
+
+struct sec_jack_platform_data {
+	void	(*set_micbias_state) (bool);
+	int	(*get_adc_value) (void);
+	struct sec_jack_zone	*zones;
+	struct sec_jack_buttons_zone	*buttons_zones;
+	int	num_zones;
+	int	num_buttons_zones;
+	int	det_gpio;
+	int	send_end_gpio;
+	bool	det_active_high;
+	bool	send_end_active_high;
+};
+#endif
+
+#endif
diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
index cc7a4e9..84f6320d 100644
--- a/include/linux/seccomp.h
+++ b/include/linux/seccomp.h
@@ -1,25 +1,90 @@
 #ifndef _LINUX_SECCOMP_H
 #define _LINUX_SECCOMP_H
 
+#include <linux/compiler.h>
+#include <linux/types.h>
 
+
+/* Valid values for seccomp.mode and prctl(PR_SET_SECCOMP, <mode>) */
+#define SECCOMP_MODE_DISABLED	0 /* seccomp is not in use. */
+#define SECCOMP_MODE_STRICT	1 /* uses hard-coded filter. */
+#define SECCOMP_MODE_FILTER	2 /* uses user-supplied filter. */
+
+/*
+ * All BPF programs must return a 32-bit value.
+ * The bottom 16-bits are for optional return data.
+ * The upper 16-bits are ordered from least permissive values to most.
+ *
+ * The ordering ensures that a min_t() over composed return values always
+ * selects the least permissive choice.
+ */
+#define SECCOMP_RET_KILL	0x00000000U /* kill the task immediately */
+#define SECCOMP_RET_TRAP	0x00030000U /* disallow and force a SIGSYS */
+#define SECCOMP_RET_ERRNO	0x00050000U /* returns an errno */
+#define SECCOMP_RET_TRACE	0x7ff00000U /* pass to a tracer or disallow */
+#define SECCOMP_RET_ALLOW	0x7fff0000U /* allow */
+
+/* Masks for the return value sections. */
+#define SECCOMP_RET_ACTION	0x7fff0000U
+#define SECCOMP_RET_DATA	0x0000ffffU
+
+/**
+ * struct seccomp_data - the format the BPF program executes over.
+ * @nr: the system call number
+ * @arch: indicates system call convention as an AUDIT_ARCH_* value
+ *        as defined in <linux/audit.h>.
+ * @instruction_pointer: at the time of the system call.
+ * @args: up to 6 system call arguments always stored as 64-bit values
+ *        regardless of the architecture.
+ */
+struct seccomp_data {
+	int nr;
+	__u32 arch;
+	__u64 instruction_pointer;
+	__u64 args[6];
+};
+
+#ifdef __KERNEL__
 #ifdef CONFIG_SECCOMP
 
 #include <linux/thread_info.h>
 #include <asm/seccomp.h>
 
-typedef struct { int mode; } seccomp_t;
+struct seccomp_filter;
+/**
+ * struct seccomp - the state of a seccomp'ed process
+ *
+ * @mode:  indicates one of the valid values above for controlled
+ *         system calls available to a process.
+ * @filter: The metadata and ruleset for determining what system calls
+ *          are allowed for a task.
+ *
+ *          @filter must only be accessed from the context of current as there
+ *          is no locking.
+ */
+struct seccomp {
+	int mode;
+	struct seccomp_filter *filter;
+};
 
-extern void __secure_computing(int);
-static inline void secure_computing(int this_syscall)
+extern int __secure_computing(int);
+static inline int secure_computing(int this_syscall)
 {
 	if (unlikely(test_thread_flag(TIF_SECCOMP)))
-		__secure_computing(this_syscall);
+		return  __secure_computing(this_syscall);
+	return 0;
+}
+
+/* A wrapper for architectures supporting only SECCOMP_MODE_STRICT. */
+static inline void secure_computing_strict(int this_syscall)
+{
+	BUG_ON(secure_computing(this_syscall) != 0);
 }
 
 extern long prctl_get_seccomp(void);
-extern long prctl_set_seccomp(unsigned long);
+extern long prctl_set_seccomp(unsigned long, char __user *);
 
-static inline int seccomp_mode(seccomp_t *s)
+static inline int seccomp_mode(struct seccomp *s)
 {
 	return s->mode;
 }
@@ -28,25 +93,41 @@
 
 #include <linux/errno.h>
 
-typedef struct { } seccomp_t;
+struct seccomp { };
+struct seccomp_filter { };
 
-#define secure_computing(x) do { } while (0)
+static inline int secure_computing(int this_syscall) { return 0; }
+static inline void secure_computing_strict(int this_syscall) { return; }
 
 static inline long prctl_get_seccomp(void)
 {
 	return -EINVAL;
 }
 
-static inline long prctl_set_seccomp(unsigned long arg2)
+static inline long prctl_set_seccomp(unsigned long arg2, char __user *arg3)
 {
 	return -EINVAL;
 }
 
-static inline int seccomp_mode(seccomp_t *s)
+static inline int seccomp_mode(struct seccomp *s)
 {
 	return 0;
 }
-
 #endif /* CONFIG_SECCOMP */
 
+#ifdef CONFIG_SECCOMP_FILTER
+extern void put_seccomp_filter(struct task_struct *tsk);
+extern void get_seccomp_filter(struct task_struct *tsk);
+extern u32 seccomp_bpf_load(int off);
+#else  /* CONFIG_SECCOMP_FILTER */
+static inline void put_seccomp_filter(struct task_struct *tsk)
+{
+	return;
+}
+static inline void get_seccomp_filter(struct task_struct *tsk)
+{
+	return;
+}
+#endif /* CONFIG_SECCOMP_FILTER */
+#endif /* __KERNEL__ */
 #endif /* _LINUX_SECCOMP_H */
diff --git a/include/linux/security.h b/include/linux/security.h
index b62f396..2a82530 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -144,6 +144,7 @@
 #define LSM_UNSAFE_SHARE	1
 #define LSM_UNSAFE_PTRACE	2
 #define LSM_UNSAFE_PTRACE_CAP	4
+#define LSM_UNSAFE_NO_NEW_PRIVS	8
 
 #ifdef CONFIG_MMU
 extern int mmap_min_addr_handler(struct ctl_table *table, int write,
diff --git a/include/linux/videodev2_exynos_media.h b/include/linux/videodev2_exynos_media.h
index 545667a..e15812b 100644
--- a/include/linux/videodev2_exynos_media.h
+++ b/include/linux/videodev2_exynos_media.h
@@ -81,6 +81,12 @@
 #define V4L2_CID_TV_SET_DVI_MODE	(V4L2_CID_EXYNOS_BASE + 57)
 #define V4L2_CID_TV_GET_DVI_MODE	(V4L2_CID_EXYNOS_BASE + 58)
 #define V4L2_CID_TV_SET_ASPECT_RATIO	(V4L2_CID_EXYNOS_BASE + 59)
+#define V4L2_CID_TV_MAX_AUDIO_CHANNELS	(V4L2_CID_EXYNOS_BASE + 60)
+#define V4L2_CID_TV_ENABLE_HDMI_AUDIO	(V4L2_CID_EXYNOS_BASE + 61)
+#define V4L2_CID_TV_SET_NUM_CHANNELS	(V4L2_CID_EXYNOS_BASE + 62)
+#define V4L2_CID_TV_UPDATE		(V4L2_CID_EXYNOS_BASE + 63)
+#define V4L2_CID_TV_SET_COLOR_RANGE	(V4L2_CID_EXYNOS_BASE + 64)
+#define V4L2_CID_TV_HDCP_ENABLE		(V4L2_CID_EXYNOS_BASE + 65)
 
 /* for color space conversion equation selection */
 #define V4L2_CID_CSC_EQ_MODE		(V4L2_CID_EXYNOS_BASE + 100)
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
index ac40716..9eb35ff 100644
--- a/include/linux/watchdog.h
+++ b/include/linux/watchdog.h
@@ -149,6 +149,7 @@
 /* drivers/watchdog/core/watchdog_core.c */
 extern int watchdog_register_device(struct watchdog_device *);
 extern void watchdog_unregister_device(struct watchdog_device *);
+extern void touch_hw_watchdog(void);
 
 #endif	/* __KERNEL__ */
 
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index af1de0f..4b96415 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -67,6 +67,7 @@
 #include <linux/syscalls.h>
 #include <linux/capability.h>
 #include <linux/fs_struct.h>
+#include <linux/compat.h>
 
 #include "audit.h"
 
@@ -2710,13 +2711,16 @@
 	audit_log_end(ab);
 }
 
-void __audit_seccomp(unsigned long syscall)
+void __audit_seccomp(unsigned long syscall, long signr, int code)
 {
 	struct audit_buffer *ab;
 
 	ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND);
-	audit_log_abend(ab, "seccomp", SIGKILL);
+	audit_log_abend(ab, "seccomp", signr);
 	audit_log_format(ab, " syscall=%ld", syscall);
+	audit_log_format(ab, " compat=%d", is_compat_task());
+	audit_log_format(ab, " ip=0x%lx", KSTK_EIP(current));
+	audit_log_format(ab, " code=0x%x", code);
 	audit_log_end(ab);
 }
 
diff --git a/kernel/fork.c b/kernel/fork.c
index bc3398e..0de7002 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -34,6 +34,7 @@
 #include <linux/cgroup.h>
 #include <linux/security.h>
 #include <linux/hugetlb.h>
+#include <linux/seccomp.h>
 #include <linux/swap.h>
 #include <linux/syscalls.h>
 #include <linux/jiffies.h>
@@ -174,6 +175,7 @@
 	free_thread_info(tsk->stack);
 	rt_mutex_debug_task_free(tsk);
 	ftrace_graph_exit_task(tsk);
+	put_seccomp_filter(tsk);
 	free_task_struct(tsk);
 }
 EXPORT_SYMBOL(free_task);
@@ -1181,6 +1183,7 @@
 		goto fork_out;
 
 	ftrace_graph_init_task(p);
+	get_seccomp_filter(p);
 
 	rt_mutex_init_task(p);
 
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index d10a575..0a5590d 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -72,6 +72,7 @@
 #include <linux/slab.h>
 #include <linux/init_task.h>
 #include <linux/binfmts.h>
+#include <linux/watchdog.h>  /* for touch_hw_watchdog() */
 
 #include <asm/switch_to.h>
 #include <asm/tlb.h>
@@ -5008,13 +5009,15 @@
 #endif
 	rcu_read_lock();
 	do_each_thread(g, p) {
-		/*
-		 * reset the NMI-timeout, listing all files on a slow
-		 * console might take a lot of time:
-		 */
-		touch_nmi_watchdog();
-		if (!state_filter || (p->state & state_filter))
+		if (!state_filter || (p->state & state_filter)) {
+			/*
+			 * reset the NMI-timeout, listing all files on a slow
+			 * console might take a lot of time:
+			 */
+			touch_nmi_watchdog();
+			touch_hw_watchdog();
 			sched_show_task(p);
+		}
 	} while_each_thread(g, p);
 
 	touch_all_softlockup_watchdogs();
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index e8d76c5..6290f6f 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -3,16 +3,357 @@
  *
  * Copyright 2004-2005  Andrea Arcangeli <andrea@cpushare.com>
  *
- * This defines a simple but solid secure-computing mode.
+ * Copyright (C) 2012 Google, Inc.
+ * Will Drewry <wad@chromium.org>
+ *
+ * This defines a simple but solid secure-computing facility.
+ *
+ * Mode 1 uses a fixed list of allowed system calls.
+ * Mode 2 allows user-defined system call filters in the form
+ *        of Berkeley Packet Filters/Linux Socket Filters.
  */
 
+#include <linux/atomic.h>
 #include <linux/audit.h>
-#include <linux/seccomp.h>
-#include <linux/sched.h>
 #include <linux/compat.h>
+#include <linux/sched.h>
+#include <linux/seccomp.h>
 
 /* #define SECCOMP_DEBUG 1 */
-#define NR_SECCOMP_MODES 1
+
+#ifdef CONFIG_SECCOMP_FILTER
+#include <asm/syscall.h>
+#include <linux/filter.h>
+#include <linux/ptrace.h>
+#include <linux/security.h>
+#include <linux/slab.h>
+#include <linux/tracehook.h>
+#include <linux/uaccess.h>
+
+/**
+ * struct seccomp_filter - container for seccomp BPF programs
+ *
+ * @usage: reference count to manage the object lifetime.
+ *         get/put helpers should be used when accessing an instance
+ *         outside of a lifetime-guarded section.  In general, this
+ *         is only needed for handling filters shared across tasks.
+ * @prev: points to a previously installed, or inherited, filter
+ * @len: the number of instructions in the program
+ * @insns: the BPF program instructions to evaluate
+ *
+ * seccomp_filter objects are organized in a tree linked via the @prev
+ * pointer.  For any task, it appears to be a singly-linked list starting
+ * with current->seccomp.filter, the most recently attached or inherited filter.
+ * However, multiple filters may share a @prev node, by way of fork(), which
+ * results in a unidirectional tree existing in memory.  This is similar to
+ * how namespaces work.
+ *
+ * seccomp_filter objects should never be modified after being attached
+ * to a task_struct (other than @usage).
+ */
+struct seccomp_filter {
+	atomic_t usage;
+	struct seccomp_filter *prev;
+	unsigned short len;  /* Instruction count */
+	struct sock_filter insns[];
+};
+
+/* Limit any path through the tree to 256KB worth of instructions. */
+#define MAX_INSNS_PER_PATH ((1 << 18) / sizeof(struct sock_filter))
+
+/**
+ * get_u32 - returns a u32 offset into data
+ * @data: a unsigned 64 bit value
+ * @index: 0 or 1 to return the first or second 32-bits
+ *
+ * This inline exists to hide the length of unsigned long.  If a 32-bit
+ * unsigned long is passed in, it will be extended and the top 32-bits will be
+ * 0. If it is a 64-bit unsigned long, then whatever data is resident will be
+ * properly returned.
+ *
+ * Endianness is explicitly ignored and left for BPF program authors to manage
+ * as per the specific architecture.
+ */
+static inline u32 get_u32(u64 data, int index)
+{
+	return ((u32 *)&data)[index];
+}
+
+/* Helper for bpf_load below. */
+#define BPF_DATA(_name) offsetof(struct seccomp_data, _name)
+/**
+ * bpf_load: checks and returns a pointer to the requested offset
+ * @off: offset into struct seccomp_data to load from
+ *
+ * Returns the requested 32-bits of data.
+ * seccomp_check_filter() should assure that @off is 32-bit aligned
+ * and not out of bounds.  Failure to do so is a BUG.
+ */
+u32 seccomp_bpf_load(int off)
+{
+	struct pt_regs *regs = task_pt_regs(current);
+	if (off == BPF_DATA(nr))
+		return syscall_get_nr(current, regs);
+	if (off == BPF_DATA(arch))
+		return syscall_get_arch(current, regs);
+	if (off >= BPF_DATA(args[0]) && off < BPF_DATA(args[6])) {
+		unsigned long value;
+		int arg = (off - BPF_DATA(args[0])) / sizeof(u64);
+		int index = !!(off % sizeof(u64));
+		syscall_get_arguments(current, regs, arg, 1, &value);
+		return get_u32(value, index);
+	}
+	if (off == BPF_DATA(instruction_pointer))
+		return get_u32(KSTK_EIP(current), 0);
+	if (off == BPF_DATA(instruction_pointer) + sizeof(u32))
+		return get_u32(KSTK_EIP(current), 1);
+	/* seccomp_check_filter should make this impossible. */
+	BUG();
+}
+
+/**
+ *	seccomp_check_filter - verify seccomp filter code
+ *	@filter: filter to verify
+ *	@flen: length of filter
+ *
+ * Takes a previously checked filter (by sk_chk_filter) and
+ * redirects all filter code that loads struct sk_buff data
+ * and related data through seccomp_bpf_load.  It also
+ * enforces length and alignment checking of those loads.
+ *
+ * Returns 0 if the rule set is legal or -EINVAL if not.
+ */
+static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
+{
+	int pc;
+	for (pc = 0; pc < flen; pc++) {
+		struct sock_filter *ftest = &filter[pc];
+		u16 code = ftest->code;
+		u32 k = ftest->k;
+
+		switch (code) {
+		case BPF_S_LD_W_ABS:
+			ftest->code = BPF_S_ANC_SECCOMP_LD_W;
+			/* 32-bit aligned and not out of bounds. */
+			if (k >= sizeof(struct seccomp_data) || k & 3)
+				return -EINVAL;
+			continue;
+		case BPF_S_LD_W_LEN:
+			ftest->code = BPF_S_LD_IMM;
+			ftest->k = sizeof(struct seccomp_data);
+			continue;
+		case BPF_S_LDX_W_LEN:
+			ftest->code = BPF_S_LDX_IMM;
+			ftest->k = sizeof(struct seccomp_data);
+			continue;
+		/* Explicitly include allowed calls. */
+		case BPF_S_RET_K:
+		case BPF_S_RET_A:
+		case BPF_S_ALU_ADD_K:
+		case BPF_S_ALU_ADD_X:
+		case BPF_S_ALU_SUB_K:
+		case BPF_S_ALU_SUB_X:
+		case BPF_S_ALU_MUL_K:
+		case BPF_S_ALU_MUL_X:
+		case BPF_S_ALU_DIV_X:
+		case BPF_S_ALU_AND_K:
+		case BPF_S_ALU_AND_X:
+		case BPF_S_ALU_OR_K:
+		case BPF_S_ALU_OR_X:
+		case BPF_S_ALU_LSH_K:
+		case BPF_S_ALU_LSH_X:
+		case BPF_S_ALU_RSH_K:
+		case BPF_S_ALU_RSH_X:
+		case BPF_S_ALU_NEG:
+		case BPF_S_LD_IMM:
+		case BPF_S_LDX_IMM:
+		case BPF_S_MISC_TAX:
+		case BPF_S_MISC_TXA:
+		case BPF_S_ALU_DIV_K:
+		case BPF_S_LD_MEM:
+		case BPF_S_LDX_MEM:
+		case BPF_S_ST:
+		case BPF_S_STX:
+		case BPF_S_JMP_JA:
+		case BPF_S_JMP_JEQ_K:
+		case BPF_S_JMP_JEQ_X:
+		case BPF_S_JMP_JGE_K:
+		case BPF_S_JMP_JGE_X:
+		case BPF_S_JMP_JGT_K:
+		case BPF_S_JMP_JGT_X:
+		case BPF_S_JMP_JSET_K:
+		case BPF_S_JMP_JSET_X:
+			continue;
+		default:
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/**
+ * seccomp_run_filters - evaluates all seccomp filters against @syscall
+ * @syscall: number of the current system call
+ *
+ * Returns valid seccomp BPF response codes.
+ */
+static u32 seccomp_run_filters(int syscall)
+{
+	struct seccomp_filter *f;
+	u32 ret = SECCOMP_RET_ALLOW;
+
+	/* Ensure unexpected behavior doesn't result in failing open. */
+	if (WARN_ON(current->seccomp.filter == NULL))
+		return SECCOMP_RET_KILL;
+
+	/*
+	 * All filters in the list are evaluated and the lowest BPF return
+	 * value always takes priority (ignoring the DATA).
+	 */
+	for (f = current->seccomp.filter; f; f = f->prev) {
+		u32 cur_ret = sk_run_filter(NULL, f->insns);
+		if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION))
+			ret = cur_ret;
+	}
+	return ret;
+}
+
+/**
+ * seccomp_attach_filter: Attaches a seccomp filter to current.
+ * @fprog: BPF program to install
+ *
+ * Returns 0 on success or an errno on failure.
+ */
+static long seccomp_attach_filter(struct sock_fprog *fprog)
+{
+	struct seccomp_filter *filter;
+	unsigned long fp_size = fprog->len * sizeof(struct sock_filter);
+	unsigned long total_insns = fprog->len;
+	long ret;
+
+	if (fprog->len == 0 || fprog->len > BPF_MAXINSNS)
+		return -EINVAL;
+
+	for (filter = current->seccomp.filter; filter; filter = filter->prev)
+		total_insns += filter->len + 4;  /* include a 4 instr penalty */
+	if (total_insns > MAX_INSNS_PER_PATH)
+		return -ENOMEM;
+
+	/*
+	 * Installing a seccomp filter requires that the task have
+	 * CAP_SYS_ADMIN in its namespace or be running with no_new_privs.
+	 * This avoids scenarios where unprivileged tasks can affect the
+	 * behavior of privileged children.
+	 */
+	if (!current->no_new_privs &&
+	    security_capable_noaudit(current_cred(), current_user_ns(),
+				     CAP_SYS_ADMIN) != 0)
+		return -EACCES;
+
+	/* Allocate a new seccomp_filter */
+	filter = kzalloc(sizeof(struct seccomp_filter) + fp_size,
+			 GFP_KERNEL|__GFP_NOWARN);
+	if (!filter)
+		return -ENOMEM;
+	atomic_set(&filter->usage, 1);
+	filter->len = fprog->len;
+
+	/* Copy the instructions from fprog. */
+	ret = -EFAULT;
+	if (copy_from_user(filter->insns, fprog->filter, fp_size))
+		goto fail;
+
+	/* Check and rewrite the fprog via the skb checker */
+	ret = sk_chk_filter(filter->insns, filter->len);
+	if (ret)
+		goto fail;
+
+	/* Check and rewrite the fprog for seccomp use */
+	ret = seccomp_check_filter(filter->insns, filter->len);
+	if (ret)
+		goto fail;
+
+	/*
+	 * If there is an existing filter, make it the prev and don't drop its
+	 * task reference.
+	 */
+	filter->prev = current->seccomp.filter;
+	current->seccomp.filter = filter;
+	return 0;
+fail:
+	kfree(filter);
+	return ret;
+}
+
+/**
+ * seccomp_attach_user_filter - attaches a user-supplied sock_fprog
+ * @user_filter: pointer to the user data containing a sock_fprog.
+ *
+ * Returns 0 on success and non-zero otherwise.
+ */
+long seccomp_attach_user_filter(char __user *user_filter)
+{
+	struct sock_fprog fprog;
+	long ret = -EFAULT;
+
+#ifdef CONFIG_COMPAT
+	if (is_compat_task()) {
+		struct compat_sock_fprog fprog32;
+		if (copy_from_user(&fprog32, user_filter, sizeof(fprog32)))
+			goto out;
+		fprog.len = fprog32.len;
+		fprog.filter = compat_ptr(fprog32.filter);
+	} else /* falls through to the if below. */
+#endif
+	if (copy_from_user(&fprog, user_filter, sizeof(fprog)))
+		goto out;
+	ret = seccomp_attach_filter(&fprog);
+out:
+	return ret;
+}
+
+/* get_seccomp_filter - increments the reference count of the filter on @tsk */
+void get_seccomp_filter(struct task_struct *tsk)
+{
+	struct seccomp_filter *orig = tsk->seccomp.filter;
+	if (!orig)
+		return;
+	/* Reference count is bounded by the number of total processes. */
+	atomic_inc(&orig->usage);
+}
+
+/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */
+void put_seccomp_filter(struct task_struct *tsk)
+{
+	struct seccomp_filter *orig = tsk->seccomp.filter;
+	/* Clean up single-reference branches iteratively. */
+	while (orig && atomic_dec_and_test(&orig->usage)) {
+		struct seccomp_filter *freeme = orig;
+		orig = orig->prev;
+		kfree(freeme);
+	}
+}
+
+/**
+ * seccomp_send_sigsys - signals the task to allow in-process syscall emulation
+ * @syscall: syscall number to send to userland
+ * @reason: filter-supplied reason code to send to userland (via si_errno)
+ *
+ * Forces a SIGSYS with a code of SYS_SECCOMP and related sigsys info.
+ */
+static void seccomp_send_sigsys(int syscall, int reason)
+{
+	struct siginfo info;
+	memset(&info, 0, sizeof(info));
+	info.si_signo = SIGSYS;
+	info.si_code = SYS_SECCOMP;
+	info.si_call_addr = (void __user *)KSTK_EIP(current);
+	info.si_errno = reason;
+	info.si_arch = syscall_get_arch(current, task_pt_regs(current));
+	info.si_syscall = syscall;
+	force_sig_info(SIGSYS, &info, current);
+}
+#endif	/* CONFIG_SECCOMP_FILTER */
 
 /*
  * Secure computing mode 1 allows only read/write/exit/sigreturn.
@@ -31,13 +372,15 @@
 };
 #endif
 
-void __secure_computing(int this_syscall)
+int __secure_computing(int this_syscall)
 {
 	int mode = current->seccomp.mode;
-	int * syscall;
+	int exit_sig = 0;
+	int *syscall;
+	u32 ret;
 
 	switch (mode) {
-	case 1:
+	case SECCOMP_MODE_STRICT:
 		syscall = mode1_syscalls;
 #ifdef CONFIG_COMPAT
 		if (is_compat_task())
@@ -45,9 +388,58 @@
 #endif
 		do {
 			if (*syscall == this_syscall)
-				return;
+				return 0;
 		} while (*++syscall);
+		exit_sig = SIGKILL;
+		ret = SECCOMP_RET_KILL;
 		break;
+#ifdef CONFIG_SECCOMP_FILTER
+	case SECCOMP_MODE_FILTER: {
+		int data;
+		ret = seccomp_run_filters(this_syscall);
+		data = ret & SECCOMP_RET_DATA;
+		ret &= SECCOMP_RET_ACTION;
+		switch (ret) {
+		case SECCOMP_RET_ERRNO:
+			/* Set the low-order 16-bits as a errno. */
+			syscall_set_return_value(current, task_pt_regs(current),
+						 -data, 0);
+			goto skip;
+		case SECCOMP_RET_TRAP:
+			/* Show the handler the original registers. */
+			syscall_rollback(current, task_pt_regs(current));
+			/* Let the filter pass back 16 bits of data. */
+			seccomp_send_sigsys(this_syscall, data);
+			goto skip;
+		case SECCOMP_RET_TRACE:
+			/* Skip these calls if there is no tracer. */
+			if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) {
+				/* Make sure userspace sees an ENOSYS. */
+				syscall_set_return_value(current,
+					task_pt_regs(current), -ENOSYS, 0);
+				goto skip;
+			}
+			/* Allow the BPF to provide the event message */
+			ptrace_event(PTRACE_EVENT_SECCOMP, data);
+			/*
+			 * The delivery of a fatal signal during event
+			 * notification may silently skip tracer notification.
+			 * Terminating the task now avoids executing a system
+			 * call that may not be intended.
+			 */
+			if (fatal_signal_pending(current))
+				break;
+			return 0;
+		case SECCOMP_RET_ALLOW:
+			return 0;
+		case SECCOMP_RET_KILL:
+		default:
+			break;
+		}
+		exit_sig = SIGSYS;
+		break;
+	}
+#endif
 	default:
 		BUG();
 	}
@@ -55,8 +447,13 @@
 #ifdef SECCOMP_DEBUG
 	dump_stack();
 #endif
-	audit_seccomp(this_syscall);
-	do_exit(SIGKILL);
+	audit_seccomp(this_syscall, exit_sig, ret);
+	do_exit(exit_sig);
+#ifdef CONFIG_SECCOMP_FILTER
+skip:
+	audit_seccomp(this_syscall, exit_sig, ret);
+#endif
+	return -1;
 }
 
 long prctl_get_seccomp(void)
@@ -64,25 +461,48 @@
 	return current->seccomp.mode;
 }
 
-long prctl_set_seccomp(unsigned long seccomp_mode)
+/**
+ * prctl_set_seccomp: configures current->seccomp.mode
+ * @seccomp_mode: requested mode to use
+ * @filter: optional struct sock_fprog for use with SECCOMP_MODE_FILTER
+ *
+ * This function may be called repeatedly with a @seccomp_mode of
+ * SECCOMP_MODE_FILTER to install additional filters.  Every filter
+ * successfully installed will be evaluated (in reverse order) for each system
+ * call the task makes.
+ *
+ * Once current->seccomp.mode is non-zero, it may not be changed.
+ *
+ * Returns 0 on success or -EINVAL on failure.
+ */
+long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter)
 {
-	long ret;
+	long ret = -EINVAL;
 
-	/* can set it only once to be even more secure */
-	ret = -EPERM;
-	if (unlikely(current->seccomp.mode))
+	if (current->seccomp.mode &&
+	    current->seccomp.mode != seccomp_mode)
 		goto out;
 
-	ret = -EINVAL;
-	if (seccomp_mode && seccomp_mode <= NR_SECCOMP_MODES) {
-		current->seccomp.mode = seccomp_mode;
-		set_thread_flag(TIF_SECCOMP);
+	switch (seccomp_mode) {
+	case SECCOMP_MODE_STRICT:
+		ret = 0;
 #ifdef TIF_NOTSC
 		disable_TSC();
 #endif
-		ret = 0;
+		break;
+#ifdef CONFIG_SECCOMP_FILTER
+	case SECCOMP_MODE_FILTER:
+		ret = seccomp_attach_user_filter(filter);
+		if (ret)
+			goto out;
+		break;
+#endif
+	default:
+		goto out;
 	}
 
- out:
+	current->seccomp.mode = seccomp_mode;
+	set_thread_flag(TIF_SECCOMP);
+out:
 	return ret;
 }
diff --git a/kernel/signal.c b/kernel/signal.c
index 98059e2..c9868a4 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -160,7 +160,7 @@
 
 #define SYNCHRONOUS_MASK \
 	(sigmask(SIGSEGV) | sigmask(SIGBUS) | sigmask(SIGILL) | \
-	 sigmask(SIGTRAP) | sigmask(SIGFPE))
+	 sigmask(SIGTRAP) | sigmask(SIGFPE) | sigmask(SIGSYS))
 
 int next_signal(struct sigpending *pending, sigset_t *mask)
 {
@@ -2708,6 +2708,13 @@
 		err |= __put_user(from->si_uid, &to->si_uid);
 		err |= __put_user(from->si_ptr, &to->si_ptr);
 		break;
+#ifdef __ARCH_SIGSYS
+	case __SI_SYS:
+		err |= __put_user(from->si_call_addr, &to->si_call_addr);
+		err |= __put_user(from->si_syscall, &to->si_syscall);
+		err |= __put_user(from->si_arch, &to->si_arch);
+		break;
+#endif
 	default: /* this is just in case for now ... */
 		err |= __put_user(from->si_pid, &to->si_pid);
 		err |= __put_user(from->si_uid, &to->si_uid);
diff --git a/kernel/sys.c b/kernel/sys.c
index b0003db..5849fff 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1911,7 +1911,7 @@
 			error = prctl_get_seccomp();
 			break;
 		case PR_SET_SECCOMP:
-			error = prctl_set_seccomp(arg2);
+			error = prctl_set_seccomp(arg2, (char __user *)arg3);
 			break;
 		case PR_GET_TSC:
 			error = GET_TSC_CTL(arg2);
@@ -1982,6 +1982,16 @@
 			error = put_user(me->signal->is_child_subreaper,
 					 (int __user *) arg2);
 			break;
+		case PR_SET_NO_NEW_PRIVS:
+			if (arg2 != 1 || arg3 || arg4 || arg5)
+				return -EINVAL;
+
+			current->no_new_privs = 1;
+			break;
+		case PR_GET_NO_NEW_PRIVS:
+			if (arg2 || arg3 || arg4 || arg5)
+				return -EINVAL;
+			return current->no_new_privs ? 1 : 0;
 		default:
 			error = -EINVAL;
 			break;
diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
index f979d85..b07241c 100644
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -37,7 +37,6 @@
 static struct alarm_base {
 	spinlock_t		lock;
 	struct timerqueue_head	timerqueue;
-	struct hrtimer		timer;
 	ktime_t			(*gettime)(void);
 	clockid_t		base_clockid;
 } alarm_bases[ALARM_NUMTYPE];
@@ -132,21 +131,17 @@
  * @base: pointer to the base where the timer is being run
  * @alarm: pointer to alarm being enqueued.
  *
- * Adds alarm to a alarm_base timerqueue and if necessary sets
- * an hrtimer to run.
+ * Adds alarm to a alarm_base timerqueue
  *
  * Must hold base->lock when calling.
  */
 static void alarmtimer_enqueue(struct alarm_base *base, struct alarm *alarm)
 {
+	if (alarm->state & ALARMTIMER_STATE_ENQUEUED)
+		timerqueue_del(&base->timerqueue, &alarm->node);
+
 	timerqueue_add(&base->timerqueue, &alarm->node);
 	alarm->state |= ALARMTIMER_STATE_ENQUEUED;
-
-	if (&alarm->node == timerqueue_getnext(&base->timerqueue)) {
-		hrtimer_try_to_cancel(&base->timer);
-		hrtimer_start(&base->timer, alarm->node.expires,
-				HRTIMER_MODE_ABS);
-	}
 }
 
 /**
@@ -154,28 +149,17 @@
  * @base: pointer to the base where the timer is running
  * @alarm: pointer to alarm being removed
  *
- * Removes alarm to a alarm_base timerqueue and if necessary sets
- * a new timer to run.
+ * Removes alarm to a alarm_base timerqueue
  *
  * Must hold base->lock when calling.
  */
 static void alarmtimer_remove(struct alarm_base *base, struct alarm *alarm)
 {
-	struct timerqueue_node *next = timerqueue_getnext(&base->timerqueue);
-
 	if (!(alarm->state & ALARMTIMER_STATE_ENQUEUED))
 		return;
 
 	timerqueue_del(&base->timerqueue, &alarm->node);
 	alarm->state &= ~ALARMTIMER_STATE_ENQUEUED;
-
-	if (next == &alarm->node) {
-		hrtimer_try_to_cancel(&base->timer);
-		next = timerqueue_getnext(&base->timerqueue);
-		if (!next)
-			return;
-		hrtimer_start(&base->timer, next->expires, HRTIMER_MODE_ABS);
-	}
 }
 
 
@@ -190,42 +174,23 @@
  */
 static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer)
 {
-	struct alarm_base *base = container_of(timer, struct alarm_base, timer);
-	struct timerqueue_node *next;
+	struct alarm *alarm = container_of(timer, struct alarm, timer);
+	struct alarm_base *base = &alarm_bases[alarm->type];
 	unsigned long flags;
-	ktime_t now;
 	int ret = HRTIMER_NORESTART;
 	int restart = ALARMTIMER_NORESTART;
 
 	spin_lock_irqsave(&base->lock, flags);
-	now = base->gettime();
-	while ((next = timerqueue_getnext(&base->timerqueue))) {
-		struct alarm *alarm;
-		ktime_t expired = next->expires;
+	alarmtimer_remove(base, alarm);
+	spin_unlock_irqrestore(&base->lock, flags);
 
-		if (expired.tv64 > now.tv64)
-			break;
+	if (alarm->function)
+		restart = alarm->function(alarm, base->gettime());
 
-		alarm = container_of(next, struct alarm, node);
-
-		timerqueue_del(&base->timerqueue, &alarm->node);
-		alarm->state &= ~ALARMTIMER_STATE_ENQUEUED;
-
-		alarm->state |= ALARMTIMER_STATE_CALLBACK;
-		spin_unlock_irqrestore(&base->lock, flags);
-		if (alarm->function)
-			restart = alarm->function(alarm, now);
-		spin_lock_irqsave(&base->lock, flags);
-		alarm->state &= ~ALARMTIMER_STATE_CALLBACK;
-
-		if (restart != ALARMTIMER_NORESTART) {
-			timerqueue_add(&base->timerqueue, &alarm->node);
-			alarm->state |= ALARMTIMER_STATE_ENQUEUED;
-		}
-	}
-
-	if (next) {
-		hrtimer_set_expires(&base->timer, next->expires);
+	spin_lock_irqsave(&base->lock, flags);
+	if (restart != ALARMTIMER_NORESTART) {
+		hrtimer_set_expires(&alarm->timer, alarm->node.expires);
+		alarmtimer_enqueue(base, alarm);
 		ret = HRTIMER_RESTART;
 	}
 	spin_unlock_irqrestore(&base->lock, flags);
@@ -331,6 +296,9 @@
 		enum alarmtimer_restart (*function)(struct alarm *, ktime_t))
 {
 	timerqueue_init(&alarm->node);
+	hrtimer_init(&alarm->timer, alarm_bases[type].base_clockid,
+			HRTIMER_MODE_ABS);
+	alarm->timer.function = alarmtimer_fired;
 	alarm->function = function;
 	alarm->type = type;
 	alarm->state = ALARMTIMER_STATE_INACTIVE;
@@ -341,17 +309,19 @@
  * @alarm: ptr to alarm to set
  * @start: time to run the alarm
  */
-void alarm_start(struct alarm *alarm, ktime_t start)
+int alarm_start(struct alarm *alarm, ktime_t start)
 {
 	struct alarm_base *base = &alarm_bases[alarm->type];
 	unsigned long flags;
+	int ret;
 
 	spin_lock_irqsave(&base->lock, flags);
-	if (alarmtimer_active(alarm))
-		alarmtimer_remove(base, alarm);
 	alarm->node.expires = start;
 	alarmtimer_enqueue(base, alarm);
+	ret = hrtimer_start(&alarm->timer, alarm->node.expires,
+				HRTIMER_MODE_ABS);
 	spin_unlock_irqrestore(&base->lock, flags);
+	return ret;
 }
 
 /**
@@ -365,18 +335,12 @@
 {
 	struct alarm_base *base = &alarm_bases[alarm->type];
 	unsigned long flags;
-	int ret = -1;
+	int ret;
+
 	spin_lock_irqsave(&base->lock, flags);
-
-	if (alarmtimer_callback_running(alarm))
-		goto out;
-
-	if (alarmtimer_is_queued(alarm)) {
+	ret = hrtimer_try_to_cancel(&alarm->timer);
+	if (ret >= 0)
 		alarmtimer_remove(base, alarm);
-		ret = 1;
-	} else
-		ret = 0;
-out:
 	spin_unlock_irqrestore(&base->lock, flags);
 	return ret;
 }
@@ -809,10 +773,6 @@
 	for (i = 0; i < ALARM_NUMTYPE; i++) {
 		timerqueue_init_head(&alarm_bases[i].timerqueue);
 		spin_lock_init(&alarm_bases[i].lock);
-		hrtimer_init(&alarm_bases[i].timer,
-				alarm_bases[i].base_clockid,
-				HRTIMER_MODE_ABS);
-		alarm_bases[i].timer.function = alarmtimer_fired;
 	}
 
 	error = alarmtimer_rtc_interface_setup();
diff --git a/net/compat.c b/net/compat.c
index ae6d67a..736bbeb 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -328,14 +328,6 @@
 	__scm_destroy(scm);
 }
 
-/*
- * A struct sock_filter is architecture independent.
- */
-struct compat_sock_fprog {
-	u16		len;
-	compat_uptr_t	filter;		/* struct sock_filter * */
-};
-
 static int do_set_attach_filter(struct socket *sock, int level, int optname,
 				char __user *optval, unsigned int optlen)
 {
diff --git a/net/core/filter.c b/net/core/filter.c
index 6f755cc..491e2e1 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -38,6 +38,7 @@
 #include <linux/filter.h>
 #include <linux/reciprocal_div.h>
 #include <linux/ratelimit.h>
+#include <linux/seccomp.h>
 
 /* No hurry in this branch
  *
@@ -352,6 +353,11 @@
 				A = 0;
 			continue;
 		}
+#ifdef CONFIG_SECCOMP_FILTER
+		case BPF_S_ANC_SECCOMP_LD_W:
+			A = seccomp_bpf_load(fentry->k);
+			continue;
+#endif
 		default:
 			WARN_RATELIMIT(1, "Unknown code:%u jt:%u tf:%u k:%u\n",
 				       fentry->code, fentry->jt,
diff --git a/samples/Makefile b/samples/Makefile
index 2f75851..5ef08bb 100644
--- a/samples/Makefile
+++ b/samples/Makefile
@@ -1,4 +1,4 @@
 # Makefile for Linux samples code
 
 obj-$(CONFIG_SAMPLES)	+= kobject/ kprobes/ tracepoints/ trace_events/ \
-			   hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/
+			   hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/
diff --git a/samples/seccomp/Makefile b/samples/seccomp/Makefile
new file mode 100644
index 0000000..16aa2d4
--- /dev/null
+++ b/samples/seccomp/Makefile
@@ -0,0 +1,32 @@
+# kbuild trick to avoid linker error. Can be omitted if a module is built.
+obj- := dummy.o
+
+hostprogs-$(CONFIG_SECCOMP_FILTER) := bpf-fancy dropper bpf-direct
+
+HOSTCFLAGS_bpf-fancy.o += -I$(objtree)/usr/include
+HOSTCFLAGS_bpf-fancy.o += -idirafter $(objtree)/include
+HOSTCFLAGS_bpf-helper.o += -I$(objtree)/usr/include
+HOSTCFLAGS_bpf-helper.o += -idirafter $(objtree)/include
+bpf-fancy-objs := bpf-fancy.o bpf-helper.o
+
+HOSTCFLAGS_dropper.o += -I$(objtree)/usr/include
+HOSTCFLAGS_dropper.o += -idirafter $(objtree)/include
+dropper-objs := dropper.o
+
+HOSTCFLAGS_bpf-direct.o += -I$(objtree)/usr/include
+HOSTCFLAGS_bpf-direct.o += -idirafter $(objtree)/include
+bpf-direct-objs := bpf-direct.o
+
+# Try to match the kernel target.
+ifeq ($(CONFIG_64BIT),)
+HOSTCFLAGS_bpf-direct.o += -m32
+HOSTCFLAGS_dropper.o += -m32
+HOSTCFLAGS_bpf-helper.o += -m32
+HOSTCFLAGS_bpf-fancy.o += -m32
+HOSTLOADLIBES_bpf-direct += -m32
+HOSTLOADLIBES_bpf-fancy += -m32
+HOSTLOADLIBES_dropper += -m32
+endif
+
+# Tell kbuild to always build the programs
+always := $(hostprogs-y)
diff --git a/samples/seccomp/bpf-direct.c b/samples/seccomp/bpf-direct.c
new file mode 100644
index 0000000..151ec3f
--- /dev/null
+++ b/samples/seccomp/bpf-direct.c
@@ -0,0 +1,190 @@
+/*
+ * Seccomp filter example for x86 (32-bit and 64-bit) with BPF macros
+ *
+ * Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org>
+ * Author: Will Drewry <wad@chromium.org>
+ *
+ * The code may be used by anyone for any purpose,
+ * and can serve as a starting point for developing
+ * applications using prctl(PR_SET_SECCOMP, 2, ...).
+ */
+#if defined(__i386__) || defined(__x86_64__)
+#define SUPPORTED_ARCH 1
+#endif
+
+#if defined(SUPPORTED_ARCH)
+#define __USE_GNU 1
+#define _GNU_SOURCE 1
+
+#include <linux/types.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+#include <linux/unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#define syscall_arg(_n) (offsetof(struct seccomp_data, args[_n]))
+#define syscall_nr (offsetof(struct seccomp_data, nr))
+
+#if defined(__i386__)
+#define REG_RESULT	REG_EAX
+#define REG_SYSCALL	REG_EAX
+#define REG_ARG0	REG_EBX
+#define REG_ARG1	REG_ECX
+#define REG_ARG2	REG_EDX
+#define REG_ARG3	REG_ESI
+#define REG_ARG4	REG_EDI
+#define REG_ARG5	REG_EBP
+#elif defined(__x86_64__)
+#define REG_RESULT	REG_RAX
+#define REG_SYSCALL	REG_RAX
+#define REG_ARG0	REG_RDI
+#define REG_ARG1	REG_RSI
+#define REG_ARG2	REG_RDX
+#define REG_ARG3	REG_R10
+#define REG_ARG4	REG_R8
+#define REG_ARG5	REG_R9
+#endif
+
+#ifndef PR_SET_NO_NEW_PRIVS
+#define PR_SET_NO_NEW_PRIVS 38
+#endif
+
+#ifndef SYS_SECCOMP
+#define SYS_SECCOMP 1
+#endif
+
+static void emulator(int nr, siginfo_t *info, void *void_context)
+{
+	ucontext_t *ctx = (ucontext_t *)(void_context);
+	int syscall;
+	char *buf;
+	ssize_t bytes;
+	size_t len;
+	if (info->si_code != SYS_SECCOMP)
+		return;
+	if (!ctx)
+		return;
+	syscall = ctx->uc_mcontext.gregs[REG_SYSCALL];
+	buf = (char *) ctx->uc_mcontext.gregs[REG_ARG1];
+	len = (size_t) ctx->uc_mcontext.gregs[REG_ARG2];
+
+	if (syscall != __NR_write)
+		return;
+	if (ctx->uc_mcontext.gregs[REG_ARG0] != STDERR_FILENO)
+		return;
+	/* Redirect stderr messages to stdout. Doesn't handle EINTR, etc */
+	ctx->uc_mcontext.gregs[REG_RESULT] = -1;
+	if (write(STDOUT_FILENO, "[ERR] ", 6) > 0) {
+		bytes = write(STDOUT_FILENO, buf, len);
+		ctx->uc_mcontext.gregs[REG_RESULT] = bytes;
+	}
+	return;
+}
+
+static int install_emulator(void)
+{
+	struct sigaction act;
+	sigset_t mask;
+	memset(&act, 0, sizeof(act));
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGSYS);
+
+	act.sa_sigaction = &emulator;
+	act.sa_flags = SA_SIGINFO;
+	if (sigaction(SIGSYS, &act, NULL) < 0) {
+		perror("sigaction");
+		return -1;
+	}
+	if (sigprocmask(SIG_UNBLOCK, &mask, NULL)) {
+		perror("sigprocmask");
+		return -1;
+	}
+	return 0;
+}
+
+static int install_filter(void)
+{
+	struct sock_filter filter[] = {
+		/* Grab the system call number */
+		BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_nr),
+		/* Jump table for the allowed syscalls */
+		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_rt_sigreturn, 0, 1),
+		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
+#ifdef __NR_sigreturn
+		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_sigreturn, 0, 1),
+		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
+#endif
+		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_exit_group, 0, 1),
+		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
+		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_exit, 0, 1),
+		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
+		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_read, 1, 0),
+		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_write, 3, 2),
+
+		/* Check that read is only using stdin. */
+		BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_arg(0)),
+		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, STDIN_FILENO, 4, 0),
+		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
+
+		/* Check that write is only using stdout */
+		BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_arg(0)),
+		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, STDOUT_FILENO, 1, 0),
+		/* Trap attempts to write to stderr */
+		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, STDERR_FILENO, 1, 2),
+
+		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
+		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRAP),
+		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
+	};
+	struct sock_fprog prog = {
+		.len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+		.filter = filter,
+	};
+
+	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+		perror("prctl(NO_NEW_PRIVS)");
+		return 1;
+	}
+
+
+	if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
+		perror("prctl");
+		return 1;
+	}
+	return 0;
+}
+
+#define payload(_c) (_c), sizeof((_c))
+int main(int argc, char **argv)
+{
+	char buf[4096];
+	ssize_t bytes = 0;
+	if (install_emulator())
+		return 1;
+	if (install_filter())
+		return 1;
+	syscall(__NR_write, STDOUT_FILENO,
+		payload("OHAI! WHAT IS YOUR NAME? "));
+	bytes = syscall(__NR_read, STDIN_FILENO, buf, sizeof(buf));
+	syscall(__NR_write, STDOUT_FILENO, payload("HELLO, "));
+	syscall(__NR_write, STDOUT_FILENO, buf, bytes);
+	syscall(__NR_write, STDERR_FILENO,
+		payload("Error message going to STDERR\n"));
+	return 0;
+}
+#else	/* SUPPORTED_ARCH */
+/*
+ * This sample is x86-only.  Since kernel samples are compiled with the
+ * host toolchain, a non-x86 host will result in using only the main()
+ * below.
+ */
+int main(void)
+{
+	return 1;
+}
+#endif	/* SUPPORTED_ARCH */
diff --git a/samples/seccomp/bpf-fancy.c b/samples/seccomp/bpf-fancy.c
new file mode 100644
index 0000000..8eb483aa
--- /dev/null
+++ b/samples/seccomp/bpf-fancy.c
@@ -0,0 +1,102 @@
+/*
+ * Seccomp BPF example using a macro-based generator.
+ *
+ * Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org>
+ * Author: Will Drewry <wad@chromium.org>
+ *
+ * The code may be used by anyone for any purpose,
+ * and can serve as a starting point for developing
+ * applications using prctl(PR_ATTACH_SECCOMP_FILTER).
+ */
+
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+#include <linux/unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include "bpf-helper.h"
+
+#ifndef PR_SET_NO_NEW_PRIVS
+#define PR_SET_NO_NEW_PRIVS 38
+#endif
+
+int main(int argc, char **argv)
+{
+	struct bpf_labels l;
+	static const char msg1[] = "Please type something: ";
+	static const char msg2[] = "You typed: ";
+	char buf[256];
+	struct sock_filter filter[] = {
+		/* TODO: LOAD_SYSCALL_NR(arch) and enforce an arch */
+		LOAD_SYSCALL_NR,
+		SYSCALL(__NR_exit, ALLOW),
+		SYSCALL(__NR_exit_group, ALLOW),
+		SYSCALL(__NR_write, JUMP(&l, write_fd)),
+		SYSCALL(__NR_read, JUMP(&l, read)),
+		DENY,  /* Don't passthrough into a label */
+
+		LABEL(&l, read),
+		ARG(0),
+		JNE(STDIN_FILENO, DENY),
+		ARG(1),
+		JNE((unsigned long)buf, DENY),
+		ARG(2),
+		JGE(sizeof(buf), DENY),
+		ALLOW,
+
+		LABEL(&l, write_fd),
+		ARG(0),
+		JEQ(STDOUT_FILENO, JUMP(&l, write_buf)),
+		JEQ(STDERR_FILENO, JUMP(&l, write_buf)),
+		DENY,
+
+		LABEL(&l, write_buf),
+		ARG(1),
+		JEQ((unsigned long)msg1, JUMP(&l, msg1_len)),
+		JEQ((unsigned long)msg2, JUMP(&l, msg2_len)),
+		JEQ((unsigned long)buf, JUMP(&l, buf_len)),
+		DENY,
+
+		LABEL(&l, msg1_len),
+		ARG(2),
+		JLT(sizeof(msg1), ALLOW),
+		DENY,
+
+		LABEL(&l, msg2_len),
+		ARG(2),
+		JLT(sizeof(msg2), ALLOW),
+		DENY,
+
+		LABEL(&l, buf_len),
+		ARG(2),
+		JLT(sizeof(buf), ALLOW),
+		DENY,
+	};
+	struct sock_fprog prog = {
+		.filter = filter,
+		.len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+	};
+	ssize_t bytes;
+	bpf_resolve_jumps(&l, filter, sizeof(filter)/sizeof(*filter));
+
+	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+		perror("prctl(NO_NEW_PRIVS)");
+		return 1;
+	}
+
+	if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
+		perror("prctl(SECCOMP)");
+		return 1;
+	}
+	syscall(__NR_write, STDOUT_FILENO, msg1, strlen(msg1));
+	bytes = syscall(__NR_read, STDIN_FILENO, buf, sizeof(buf)-1);
+	bytes = (bytes > 0 ? bytes : 0);
+	syscall(__NR_write, STDERR_FILENO, msg2, strlen(msg2));
+	syscall(__NR_write, STDERR_FILENO, buf, bytes);
+	/* Now get killed */
+	syscall(__NR_write, STDERR_FILENO, msg2, strlen(msg2)+2);
+	return 0;
+}
diff --git a/samples/seccomp/bpf-helper.c b/samples/seccomp/bpf-helper.c
new file mode 100644
index 0000000..579cfe3
--- /dev/null
+++ b/samples/seccomp/bpf-helper.c
@@ -0,0 +1,89 @@
+/*
+ * Seccomp BPF helper functions
+ *
+ * Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org>
+ * Author: Will Drewry <wad@chromium.org>
+ *
+ * The code may be used by anyone for any purpose,
+ * and can serve as a starting point for developing
+ * applications using prctl(PR_ATTACH_SECCOMP_FILTER).
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "bpf-helper.h"
+
+int bpf_resolve_jumps(struct bpf_labels *labels,
+		      struct sock_filter *filter, size_t count)
+{
+	struct sock_filter *begin = filter;
+	__u8 insn = count - 1;
+
+	if (count < 1)
+		return -1;
+	/*
+	* Walk it once, backwards, to build the label table and do fixups.
+	* Since backward jumps are disallowed by BPF, this is easy.
+	*/
+	filter += insn;
+	for (; filter >= begin; --insn, --filter) {
+		if (filter->code != (BPF_JMP+BPF_JA))
+			continue;
+		switch ((filter->jt<<8)|filter->jf) {
+		case (JUMP_JT<<8)|JUMP_JF:
+			if (labels->labels[filter->k].location == 0xffffffff) {
+				fprintf(stderr, "Unresolved label: '%s'\n",
+					labels->labels[filter->k].label);
+				return 1;
+			}
+			filter->k = labels->labels[filter->k].location -
+				    (insn + 1);
+			filter->jt = 0;
+			filter->jf = 0;
+			continue;
+		case (LABEL_JT<<8)|LABEL_JF:
+			if (labels->labels[filter->k].location != 0xffffffff) {
+				fprintf(stderr, "Duplicate label use: '%s'\n",
+					labels->labels[filter->k].label);
+				return 1;
+			}
+			labels->labels[filter->k].location = insn;
+			filter->k = 0; /* fall through */
+			filter->jt = 0;
+			filter->jf = 0;
+			continue;
+		}
+	}
+	return 0;
+}
+
+/* Simple lookup table for labels. */
+__u32 seccomp_bpf_label(struct bpf_labels *labels, const char *label)
+{
+	struct __bpf_label *begin = labels->labels, *end;
+	int id;
+	if (labels->count == 0) {
+		begin->label = label;
+		begin->location = 0xffffffff;
+		labels->count++;
+		return 0;
+	}
+	end = begin + labels->count;
+	for (id = 0; begin < end; ++begin, ++id) {
+		if (!strcmp(label, begin->label))
+			return id;
+	}
+	begin->label = label;
+	begin->location = 0xffffffff;
+	labels->count++;
+	return id;
+}
+
+void seccomp_bpf_print(struct sock_filter *filter, size_t count)
+{
+	struct sock_filter *end = filter + count;
+	for ( ; filter < end; ++filter)
+		printf("{ code=%u,jt=%u,jf=%u,k=%u },\n",
+			filter->code, filter->jt, filter->jf, filter->k);
+}
diff --git a/samples/seccomp/bpf-helper.h b/samples/seccomp/bpf-helper.h
new file mode 100644
index 0000000..643279d
--- /dev/null
+++ b/samples/seccomp/bpf-helper.h
@@ -0,0 +1,238 @@
+/*
+ * Example wrapper around BPF macros.
+ *
+ * Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org>
+ * Author: Will Drewry <wad@chromium.org>
+ *
+ * The code may be used by anyone for any purpose,
+ * and can serve as a starting point for developing
+ * applications using prctl(PR_SET_SECCOMP, 2, ...).
+ *
+ * No guarantees are provided with respect to the correctness
+ * or functionality of this code.
+ */
+#ifndef __BPF_HELPER_H__
+#define __BPF_HELPER_H__
+
+#include <asm/bitsperlong.h>	/* for __BITS_PER_LONG */
+#include <endian.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>	/* for seccomp_data */
+#include <linux/types.h>
+#include <linux/unistd.h>
+#include <stddef.h>
+
+#define BPF_LABELS_MAX 256
+struct bpf_labels {
+	int count;
+	struct __bpf_label {
+		const char *label;
+		__u32 location;
+	} labels[BPF_LABELS_MAX];
+};
+
+int bpf_resolve_jumps(struct bpf_labels *labels,
+		      struct sock_filter *filter, size_t count);
+__u32 seccomp_bpf_label(struct bpf_labels *labels, const char *label);
+void seccomp_bpf_print(struct sock_filter *filter, size_t count);
+
+#define JUMP_JT 0xff
+#define JUMP_JF 0xff
+#define LABEL_JT 0xfe
+#define LABEL_JF 0xfe
+
+#define ALLOW \
+	BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
+#define DENY \
+	BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL)
+#define JUMP(labels, label) \
+	BPF_JUMP(BPF_JMP+BPF_JA, FIND_LABEL((labels), (label)), \
+		 JUMP_JT, JUMP_JF)
+#define LABEL(labels, label) \
+	BPF_JUMP(BPF_JMP+BPF_JA, FIND_LABEL((labels), (label)), \
+		 LABEL_JT, LABEL_JF)
+#define SYSCALL(nr, jt) \
+	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (nr), 0, 1), \
+	jt
+
+/* Lame, but just an example */
+#define FIND_LABEL(labels, label) seccomp_bpf_label((labels), #label)
+
+#define EXPAND(...) __VA_ARGS__
+/* Map all width-sensitive operations */
+#if __BITS_PER_LONG == 32
+
+#define JEQ(x, jt) JEQ32(x, EXPAND(jt))
+#define JNE(x, jt) JNE32(x, EXPAND(jt))
+#define JGT(x, jt) JGT32(x, EXPAND(jt))
+#define JLT(x, jt) JLT32(x, EXPAND(jt))
+#define JGE(x, jt) JGE32(x, EXPAND(jt))
+#define JLE(x, jt) JLE32(x, EXPAND(jt))
+#define JA(x, jt) JA32(x, EXPAND(jt))
+#define ARG(i) ARG_32(i)
+#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
+
+#elif __BITS_PER_LONG == 64
+
+/* Ensure that we load the logically correct offset. */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define ENDIAN(_lo, _hi) _lo, _hi
+#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
+#define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define ENDIAN(_lo, _hi) _hi, _lo
+#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32)
+#define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
+#else
+#error "Unknown endianness"
+#endif
+
+union arg64 {
+	struct {
+		__u32 ENDIAN(lo32, hi32);
+	};
+	__u64 u64;
+};
+
+#define JEQ(x, jt) \
+	JEQ64(((union arg64){.u64 = (x)}).lo32, \
+	      ((union arg64){.u64 = (x)}).hi32, \
+	      EXPAND(jt))
+#define JGT(x, jt) \
+	JGT64(((union arg64){.u64 = (x)}).lo32, \
+	      ((union arg64){.u64 = (x)}).hi32, \
+	      EXPAND(jt))
+#define JGE(x, jt) \
+	JGE64(((union arg64){.u64 = (x)}).lo32, \
+	      ((union arg64){.u64 = (x)}).hi32, \
+	      EXPAND(jt))
+#define JNE(x, jt) \
+	JNE64(((union arg64){.u64 = (x)}).lo32, \
+	      ((union arg64){.u64 = (x)}).hi32, \
+	      EXPAND(jt))
+#define JLT(x, jt) \
+	JLT64(((union arg64){.u64 = (x)}).lo32, \
+	      ((union arg64){.u64 = (x)}).hi32, \
+	      EXPAND(jt))
+#define JLE(x, jt) \
+	JLE64(((union arg64){.u64 = (x)}).lo32, \
+	      ((union arg64){.u64 = (x)}).hi32, \
+	      EXPAND(jt))
+
+#define JA(x, jt) \
+	JA64(((union arg64){.u64 = (x)}).lo32, \
+	       ((union arg64){.u64 = (x)}).hi32, \
+	       EXPAND(jt))
+#define ARG(i) ARG_64(i)
+
+#else
+#error __BITS_PER_LONG value unusable.
+#endif
+
+/* Loads the arg into A */
+#define ARG_32(idx) \
+	BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx))
+
+/* Loads hi into A and lo in X */
+#define ARG_64(idx) \
+	BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx)), \
+	BPF_STMT(BPF_ST, 0), /* lo -> M[0] */ \
+	BPF_STMT(BPF_LD+BPF_W+BPF_ABS, HI_ARG(idx)), \
+	BPF_STMT(BPF_ST, 1) /* hi -> M[1] */
+
+#define JEQ32(value, jt) \
+	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (value), 0, 1), \
+	jt
+
+#define JNE32(value, jt) \
+	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (value), 1, 0), \
+	jt
+
+/* Checks the lo, then swaps to check the hi. A=lo,X=hi */
+#define JEQ64(lo, hi, jt) \
+	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
+	BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
+	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 0, 2), \
+	BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
+	jt, \
+	BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
+
+#define JNE64(lo, hi, jt) \
+	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 5, 0), \
+	BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
+	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 2, 0), \
+	BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
+	jt, \
+	BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
+
+#define JA32(value, jt) \
+	BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (value), 0, 1), \
+	jt
+
+#define JA64(lo, hi, jt) \
+	BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (hi), 3, 0), \
+	BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
+	BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (lo), 0, 2), \
+	BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
+	jt, \
+	BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
+
+#define JGE32(value, jt) \
+	BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 0, 1), \
+	jt
+
+#define JLT32(value, jt) \
+	BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 1, 0), \
+	jt
+
+/* Shortcut checking if hi > arg.hi. */
+#define JGE64(lo, hi, jt) \
+	BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \
+	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
+	BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
+	BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (lo), 0, 2), \
+	BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
+	jt, \
+	BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
+
+#define JLT64(lo, hi, jt) \
+	BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \
+	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
+	BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
+	BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 2, 0), \
+	BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
+	jt, \
+	BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
+
+#define JGT32(value, jt) \
+	BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 0, 1), \
+	jt
+
+#define JLE32(value, jt) \
+	BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 1, 0), \
+	jt
+
+/* Check hi > args.hi first, then do the GE checking */
+#define JGT64(lo, hi, jt) \
+	BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \
+	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
+	BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
+	BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 0, 2), \
+	BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
+	jt, \
+	BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
+
+#define JLE64(lo, hi, jt) \
+	BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 6, 0), \
+	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 3), \
+	BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
+	BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 2, 0), \
+	BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
+	jt, \
+	BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
+
+#define LOAD_SYSCALL_NR \
+	BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \
+		 offsetof(struct seccomp_data, nr))
+
+#endif  /* __BPF_HELPER_H__ */
diff --git a/samples/seccomp/dropper.c b/samples/seccomp/dropper.c
new file mode 100644
index 0000000..c69c347
--- /dev/null
+++ b/samples/seccomp/dropper.c
@@ -0,0 +1,68 @@
+/*
+ * Naive system call dropper built on seccomp_filter.
+ *
+ * Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org>
+ * Author: Will Drewry <wad@chromium.org>
+ *
+ * The code may be used by anyone for any purpose,
+ * and can serve as a starting point for developing
+ * applications using prctl(PR_SET_SECCOMP, 2, ...).
+ *
+ * When run, returns the specified errno for the specified
+ * system call number against the given architecture.
+ *
+ * Run this one as root as PR_SET_NO_NEW_PRIVS is not called.
+ */
+
+#include <errno.h>
+#include <linux/audit.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+#include <linux/unistd.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+static int install_filter(int nr, int arch, int error)
+{
+	struct sock_filter filter[] = {
+		BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
+			 (offsetof(struct seccomp_data, arch))),
+		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, arch, 0, 3),
+		BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
+			 (offsetof(struct seccomp_data, nr))),
+		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, nr, 0, 1),
+		BPF_STMT(BPF_RET+BPF_K,
+			 SECCOMP_RET_ERRNO|(error & SECCOMP_RET_DATA)),
+		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
+	};
+	struct sock_fprog prog = {
+		.len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+		.filter = filter,
+	};
+	if (prctl(PR_SET_SECCOMP, 2, &prog)) {
+		perror("prctl");
+		return 1;
+	}
+	return 0;
+}
+
+int main(int argc, char **argv)
+{
+	if (argc < 5) {
+		fprintf(stderr, "Usage:\n"
+			"dropper <syscall_nr> <arch> <errno> <prog> [<args>]\n"
+			"Hint:	AUDIT_ARCH_I386: 0x%X\n"
+			"	AUDIT_ARCH_X86_64: 0x%X\n"
+			"\n", AUDIT_ARCH_I386, AUDIT_ARCH_X86_64);
+		return 1;
+	}
+	if (install_filter(strtol(argv[1], NULL, 0), strtol(argv[2], NULL, 0),
+			   strtol(argv[3], NULL, 0)))
+		return 1;
+	execv(argv[4], &argv[4]);
+	printf("Failed to execv\n");
+	return 255;
+}
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 6327685..b81ea10 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -394,6 +394,11 @@
 			new_profile = find_attach(ns, &ns->base.profiles, name);
 		if (!new_profile)
 			goto cleanup;
+		/*
+		 * NOTE: Domain transitions from unconfined are allowed
+		 * even when no_new_privs is set because this aways results
+		 * in a further reduction of permissions.
+		 */
 		goto apply;
 	}
 
@@ -455,6 +460,16 @@
 		/* fail exec */
 		error = -EACCES;
 
+	/*
+	 * Policy has specified a domain transition, if no_new_privs then
+	 * fail the exec.
+	 */
+	if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) {
+		aa_put_profile(new_profile);
+		error = -EPERM;
+		goto cleanup;
+	}
+
 	if (!new_profile)
 		goto audit;
 
@@ -609,6 +624,14 @@
 	const char *target = NULL, *info = NULL;
 	int error = 0;
 
+	/*
+	 * Fail explicitly requested domain transitions if no_new_privs.
+	 * There is no exception for unconfined as change_hat is not
+	 * available.
+	 */
+	if (current->no_new_privs)
+		return -EPERM;
+
 	/* released below */
 	cred = get_current_cred();
 	cxt = cred->security;
@@ -750,6 +773,18 @@
 	cxt = cred->security;
 	profile = aa_cred_profile(cred);
 
+	/*
+	 * Fail explicitly requested domain transitions if no_new_privs
+	 * and not unconfined.
+	 * Domain transitions from unconfined are allowed even when
+	 * no_new_privs is set because this aways results in a reduction
+	 * of permissions.
+	 */
+	if (current->no_new_privs && !unconfined(profile)) {
+		put_cred(cred);
+		return -EPERM;
+	}
+
 	if (ns_name) {
 		/* released below */
 		ns = aa_find_namespace(profile->ns, ns_name);
diff --git a/security/commoncap.c b/security/commoncap.c
index 0051ac2..98ff463 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -523,14 +523,17 @@
 
 
 	/* Don't let someone trace a set[ug]id/setpcap binary with the revised
-	 * credentials unless they have the appropriate permit
+	 * credentials unless they have the appropriate permit.
+	 *
+	 * In addition, if NO_NEW_PRIVS, then ensure we get no new privs.
 	 */
 	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)) {
+		if (!capable(CAP_SETUID) ||
+		    (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) {
 			new->euid = new->uid;
 			new->egid = new->gid;
 		}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 1ae096e..9f22cf8 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2077,6 +2077,13 @@
 		new_tsec->sid = old_tsec->exec_sid;
 		/* Reset exec SID on execve. */
 		new_tsec->exec_sid = 0;
+
+		/*
+		 * Minimize confusion: if no_new_privs and a transition is
+		 * explicitly requested, then fail the exec.
+		 */
+		if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)
+			return -EPERM;
 	} else {
 		/* Check for a default transition on this program. */
 		rc = security_transition_sid(old_tsec->sid, isec->sid,
@@ -2090,7 +2097,8 @@
 	ad.selinux_audit_data = &sad;
 	ad.u.path = bprm->file->f_path;
 
-	if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
+	if ((bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) ||
+	    (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS))
 		new_tsec->sid = old_tsec->sid;
 
 	if (new_tsec->sid == old_tsec->sid) {
diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c
index 1332692..00121ba 100644
--- a/sound/soc/codecs/wm8958-dsp2.c
+++ b/sound/soc/codecs/wm8958-dsp2.c
@@ -946,7 +946,7 @@
 		wm8994->mbc_texts = kmalloc(sizeof(char *)
 					    * pdata->num_mbc_cfgs, GFP_KERNEL);
 		if (!wm8994->mbc_texts) {
-			dev_err(wm8994->codec->dev,
+			dev_err(wm8994->hubs.codec->dev,
 				"Failed to allocate %d MBC config texts\n",
 				pdata->num_mbc_cfgs);
 			return;
@@ -958,9 +958,10 @@
 		wm8994->mbc_enum.max = pdata->num_mbc_cfgs;
 		wm8994->mbc_enum.texts = wm8994->mbc_texts;
 
-		ret = snd_soc_add_codec_controls(wm8994->codec, control, 1);
+		ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
+						 control, 1);
 		if (ret != 0)
-			dev_err(wm8994->codec->dev,
+			dev_err(wm8994->hubs.codec->dev,
 				"Failed to add MBC mode controls: %d\n", ret);
 	}
 
@@ -974,7 +975,7 @@
 		wm8994->vss_texts = kmalloc(sizeof(char *)
 					    * pdata->num_vss_cfgs, GFP_KERNEL);
 		if (!wm8994->vss_texts) {
-			dev_err(wm8994->codec->dev,
+			dev_err(wm8994->hubs.codec->dev,
 				"Failed to allocate %d VSS config texts\n",
 				pdata->num_vss_cfgs);
 			return;
@@ -986,9 +987,10 @@
 		wm8994->vss_enum.max = pdata->num_vss_cfgs;
 		wm8994->vss_enum.texts = wm8994->vss_texts;
 
-		ret = snd_soc_add_codec_controls(wm8994->codec, control, 1);
+		ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
+						 control, 1);
 		if (ret != 0)
-			dev_err(wm8994->codec->dev,
+			dev_err(wm8994->hubs.codec->dev,
 				"Failed to add VSS mode controls: %d\n", ret);
 	}
 
@@ -1003,7 +1005,7 @@
 		wm8994->vss_hpf_texts = kmalloc(sizeof(char *)
 						* pdata->num_vss_hpf_cfgs, GFP_KERNEL);
 		if (!wm8994->vss_hpf_texts) {
-			dev_err(wm8994->codec->dev,
+			dev_err(wm8994->hubs.codec->dev,
 				"Failed to allocate %d VSS HPF config texts\n",
 				pdata->num_vss_hpf_cfgs);
 			return;
@@ -1015,9 +1017,10 @@
 		wm8994->vss_hpf_enum.max = pdata->num_vss_hpf_cfgs;
 		wm8994->vss_hpf_enum.texts = wm8994->vss_hpf_texts;
 
-		ret = snd_soc_add_codec_controls(wm8994->codec, control, 1);
+		ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
+						 control, 1);
 		if (ret != 0)
-			dev_err(wm8994->codec->dev,
+			dev_err(wm8994->hubs.codec->dev,
 				"Failed to add VSS HPFmode controls: %d\n",
 				ret);
 	}
@@ -1033,7 +1036,7 @@
 		wm8994->enh_eq_texts = kmalloc(sizeof(char *)
 						* pdata->num_enh_eq_cfgs, GFP_KERNEL);
 		if (!wm8994->enh_eq_texts) {
-			dev_err(wm8994->codec->dev,
+			dev_err(wm8994->hubs.codec->dev,
 				"Failed to allocate %d enhanced EQ config texts\n",
 				pdata->num_enh_eq_cfgs);
 			return;
@@ -1045,9 +1048,10 @@
 		wm8994->enh_eq_enum.max = pdata->num_enh_eq_cfgs;
 		wm8994->enh_eq_enum.texts = wm8994->enh_eq_texts;
 
-		ret = snd_soc_add_codec_controls(wm8994->codec, control, 1);
+		ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
+						 control, 1);
 		if (ret != 0)
-			dev_err(wm8994->codec->dev,
+			dev_err(wm8994->hubs.codec->dev,
 				"Failed to add enhanced EQ controls: %d\n",
 				ret);
 	}
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index d256a93..36acfcc 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -218,7 +218,6 @@
 	unsigned int sysclk_rate;
 	unsigned int fs;
 	unsigned int bclk;
-	int class_w_users;
 	unsigned int fll_fref;
 	unsigned int fll_fout;
 	int fll_src;
@@ -824,84 +823,6 @@
 	return 0;
 }
 
-/*
- * When used with DAC outputs only the WM8993 charge pump supports
- * operation in class W mode, providing very low power consumption
- * when used with digital sources.  Enable and disable this mode
- * automatically depending on the mixer configuration.
- *
- * Currently the only supported paths are the direct DAC->headphone
- * paths (which provide minimum power consumption anyway).
- */
-static int class_w_put(struct snd_kcontrol *kcontrol,
-		       struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
-	struct snd_soc_codec *codec = widget->codec;
-	struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
-	int ret;
-
-	/* Turn it off if we're using the main output mixer */
-	if (ucontrol->value.integer.value[0] == 0) {
-		if (wm8993->class_w_users == 0) {
-			dev_dbg(codec->dev, "Disabling Class W\n");
-			snd_soc_update_bits(codec, WM8993_CLASS_W_0,
-					    WM8993_CP_DYN_FREQ |
-					    WM8993_CP_DYN_V,
-					    0);
-		}
-		wm8993->class_w_users++;
-		wm8993->hubs_data.class_w = true;
-	}
-
-	/* Implement the change */
-	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
-
-	/* Enable it if we're using the direct DAC path */
-	if (ucontrol->value.integer.value[0] == 1) {
-		if (wm8993->class_w_users == 1) {
-			dev_dbg(codec->dev, "Enabling Class W\n");
-			snd_soc_update_bits(codec, WM8993_CLASS_W_0,
-					    WM8993_CP_DYN_FREQ |
-					    WM8993_CP_DYN_V,
-					    WM8993_CP_DYN_FREQ |
-					    WM8993_CP_DYN_V);
-		}
-		wm8993->class_w_users--;
-		wm8993->hubs_data.class_w = false;
-	}
-
-	dev_dbg(codec->dev, "Indirect DAC use count now %d\n",
-		wm8993->class_w_users);
-
-	return ret;
-}
-
-#define SOC_DAPM_ENUM_W(xname, xenum) \
-{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-	.info = snd_soc_info_enum_double, \
-	.get = snd_soc_dapm_get_enum_double, \
-	.put = class_w_put, \
-	.private_value = (unsigned long)&xenum }
-
-static const char *hp_mux_text[] = {
-	"Mixer",
-	"DAC",
-};
-
-static const struct soc_enum hpl_enum =
-	SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text);
-
-static const struct snd_kcontrol_new hpl_mux =
-	SOC_DAPM_ENUM_W("Left Headphone Mux", hpl_enum);
-
-static const struct soc_enum hpr_enum =
-	SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text);
-
-static const struct snd_kcontrol_new hpr_mux =
-	SOC_DAPM_ENUM_W("Right Headphone Mux", hpr_enum);
-
 static const struct snd_kcontrol_new left_speaker_mixer[] = {
 SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0),
 SOC_DAPM_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0),
@@ -988,8 +909,8 @@
 SND_SOC_DAPM_DAC("DACL", NULL, WM8993_POWER_MANAGEMENT_3, 1, 0),
 SND_SOC_DAPM_DAC("DACR", NULL, WM8993_POWER_MANAGEMENT_3, 0, 0),
 
-SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
-SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux),
 
 SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0,
 		   left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
@@ -1579,9 +1500,6 @@
 		return ret;
 	}
 
-	/* By default we're using the output mixers */
-	wm8993->class_w_users = 2;
-
 	/* Latch volume update bits and default ZC on */
 	snd_soc_update_bits(codec, WM8993_RIGHT_DAC_DIGITAL_VOLUME,
 			    WM8993_DAC_VU, WM8993_DAC_VU);
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 539c074..43611b5 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -91,8 +91,6 @@
 	WM8994_AIF2_EQ_GAINS_1,
 };
 
-static void wm8958_default_micdet(u16 status, void *data);
-
 static const struct wm8958_micd_rate micdet_rates[] = {
 	{ 32768,       true,  1, 4 },
 	{ 32768,       false, 1, 1 },
@@ -103,8 +101,8 @@
 static const struct wm8958_micd_rate jackdet_rates[] = {
 	{ 32768,       true,  0, 1 },
 	{ 32768,       false, 0, 1 },
-	{ 44100 * 256, true,  7, 10 },
-	{ 44100 * 256, false, 7, 10 },
+	{ 44100 * 256, true,  10, 10 },
+	{ 44100 * 256, false, 7, 8 },
 };
 
 static void wm8958_micd_set_rate(struct snd_soc_codec *codec)
@@ -115,9 +113,6 @@
 	const struct wm8958_micd_rate *rates;
 	int num_rates;
 
-	if (wm8994->jack_cb != wm8958_default_micdet)
-		return;
-
 	idle = !wm8994->jack_mic;
 
 	sysclk = snd_soc_read(codec, WM8994_CLOCKING_1);
@@ -151,6 +146,10 @@
 	val = rates[best].start << WM8958_MICD_BIAS_STARTTIME_SHIFT
 		| rates[best].rate << WM8958_MICD_RATE_SHIFT;
 
+	dev_dbg(codec->dev, "MICD rate %d,%d for %dHz %s\n",
+		rates[best].start, rates[best].rate, sysclk,
+		idle ? "idle" : "active");
+
 	snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
 			    WM8958_MICD_BIAS_STARTTIME_MASK |
 			    WM8958_MICD_RATE_MASK, val);
@@ -431,7 +430,7 @@
 		wm8994->dac_rates[iface]);
 
 	/* The EQ will be disabled while reconfiguring it, remember the
-	 * current configuration. 
+	 * current configuration.
 	 */
 	save = snd_soc_read(codec, base);
 	save &= WM8994_AIF1DAC1_EQ_ENA;
@@ -666,6 +665,18 @@
 	       eq_tlv),
 };
 
+static const struct snd_kcontrol_new wm8994_drc_controls[] = {
+SND_SOC_BYTES_MASK("AIF1.1 DRC", WM8994_AIF1_DRC1_1, 5,
+		   WM8994_AIF1DAC1_DRC_ENA | WM8994_AIF1ADC1L_DRC_ENA |
+		   WM8994_AIF1ADC1R_DRC_ENA),
+SND_SOC_BYTES_MASK("AIF1.2 DRC", WM8994_AIF1_DRC2_1, 5,
+		   WM8994_AIF1DAC2_DRC_ENA | WM8994_AIF1ADC2L_DRC_ENA |
+		   WM8994_AIF1ADC2R_DRC_ENA),
+SND_SOC_BYTES_MASK("AIF2 DRC", WM8994_AIF2_DRC_1, 5,
+		   WM8994_AIF2DAC_DRC_ENA | WM8994_AIF2ADCL_DRC_ENA |
+		   WM8994_AIF2ADCR_DRC_ENA),
+};
+
 static const char *wm8958_ng_text[] = {
 	"30ms", "125ms", "250ms", "500ms",
 };
@@ -719,7 +730,7 @@
 {
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
-	if (!wm8994->jackdet || !wm8994->jack_cb)
+	if (!wm8994->jackdet || !wm8994->micdet[0].jack)
 		return;
 
 	if (wm8994->active_refcount)
@@ -784,11 +795,27 @@
 			 struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = w->codec;
+	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
 		return configure_clock(codec);
 
+	case SND_SOC_DAPM_POST_PMU:
+		/*
+		 * JACKDET won't run until we start the clock and it
+		 * only reports deltas, make sure we notify the state
+		 * up the stack on startup.  Use a *very* generous
+		 * timeout for paranoia, there's no urgency and we
+		 * don't want false reports.
+		 */
+		if (wm8994->jackdet && !wm8994->clk_has_run) {
+			schedule_delayed_work(&wm8994->jackdet_bootstrap,
+					      msecs_to_jiffies(1000));
+			wm8994->clk_has_run = true;
+		}
+		break;
+
 	case SND_SOC_DAPM_POST_PMD:
 		configure_clock(codec);
 		break;
@@ -817,7 +844,7 @@
 
 		switch (wm8994->vmid_mode) {
 		default:
-			WARN_ON(0 == "Invalid VMID mode");
+			WARN_ON(NULL == "Invalid VMID mode");
 		case WM8994_VMID_NORMAL:
 			/* Startup bias, VMID ramp & buffer */
 			snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
@@ -970,27 +997,12 @@
 	return 0;
 }
 
-static void wm8994_update_class_w(struct snd_soc_codec *codec)
+static bool wm8994_check_class_w_digital(struct snd_soc_codec *codec)
 {
-	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
-	int enable = 1;
 	int source = 0;  /* GCC flow analysis can't track enable */
 	int reg, reg_r;
 
-	/* Only support direct DAC->headphone paths */
-	reg = snd_soc_read(codec, WM8994_OUTPUT_MIXER_1);
-	if (!(reg & WM8994_DAC1L_TO_HPOUT1L)) {
-		dev_vdbg(codec->dev, "HPL connected to output mixer\n");
-		enable = 0;
-	}
-
-	reg = snd_soc_read(codec, WM8994_OUTPUT_MIXER_2);
-	if (!(reg & WM8994_DAC1R_TO_HPOUT1R)) {
-		dev_vdbg(codec->dev, "HPR connected to output mixer\n");
-		enable = 0;
-	}
-
-	/* We also need the same setting for L/R and only one path */
+	/* We also need the same AIF source for L/R and only one path */
 	reg = snd_soc_read(codec, WM8994_DAC1_LEFT_MIXER_ROUTING);
 	switch (reg) {
 	case WM8994_AIF2DACL_TO_DAC1L:
@@ -1007,36 +1019,27 @@
 		break;
 	default:
 		dev_vdbg(codec->dev, "DAC mixer setting: %x\n", reg);
-		enable = 0;
-		break;
+		return false;
 	}
 
 	reg_r = snd_soc_read(codec, WM8994_DAC1_RIGHT_MIXER_ROUTING);
 	if (reg_r != reg) {
 		dev_vdbg(codec->dev, "Left and right DAC mixers different\n");
-		enable = 0;
+		return false;
 	}
 
-	if (enable) {
-		dev_dbg(codec->dev, "Class W enabled\n");
-		snd_soc_update_bits(codec, WM8994_CLASS_W_1,
-				    WM8994_CP_DYN_PWR |
-				    WM8994_CP_DYN_SRC_SEL_MASK,
-				    source | WM8994_CP_DYN_PWR);
-		wm8994->hubs.class_w = true;
-		
-	} else {
-		dev_dbg(codec->dev, "Class W disabled\n");
-		snd_soc_update_bits(codec, WM8994_CLASS_W_1,
-				    WM8994_CP_DYN_PWR, 0);
-		wm8994->hubs.class_w = false;
-	}
+	/* Set the source up */
+	snd_soc_update_bits(codec, WM8994_CLASS_W_1,
+			    WM8994_CP_DYN_SRC_SEL_MASK, source);
+
+	return true;
 }
 
 static int aif1clk_ev(struct snd_soc_dapm_widget *w,
 		      struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = w->codec;
+	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 	struct wm8994 *control = codec->control_data;
 	int mask = WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA;
 	int i;
@@ -1055,6 +1058,10 @@
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
+		/* Don't enable timeslot 2 if not in use */
+		if (wm8994->channels[0] <= 2)
+			mask &= ~(WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
+
 		val = snd_soc_read(codec, WM8994_AIF1_CONTROL_1);
 		if ((val & WM8994_AIF1ADCL_SRC) &&
 		    (val & WM8994_AIF1ADCR_SRC))
@@ -1333,45 +1340,6 @@
 	return 0;
 }
 
-static const char *hp_mux_text[] = {
-	"Mixer",
-	"DAC",
-};
-
-#define WM8994_HP_ENUM(xname, xenum) \
-{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-	.info = snd_soc_info_enum_double, \
- 	.get = snd_soc_dapm_get_enum_double, \
- 	.put = wm8994_put_hp_enum, \
-  	.private_value = (unsigned long)&xenum }
-
-static int wm8994_put_hp_enum(struct snd_kcontrol *kcontrol,
-			      struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-	struct snd_soc_dapm_widget *w = wlist->widgets[0];
-	struct snd_soc_codec *codec = w->codec;
-	int ret;
-
-	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
-
-	wm8994_update_class_w(codec);
-
-	return ret;
-}
-
-static const struct soc_enum hpl_enum =
-	SOC_ENUM_SINGLE(WM8994_OUTPUT_MIXER_1, 8, 2, hp_mux_text);
-
-static const struct snd_kcontrol_new hpl_mux =
-	WM8994_HP_ENUM("Left Headphone Mux", hpl_enum);
-
-static const struct soc_enum hpr_enum =
-	SOC_ENUM_SINGLE(WM8994_OUTPUT_MIXER_2, 8, 2, hp_mux_text);
-
-static const struct snd_kcontrol_new hpr_mux =
-	WM8994_HP_ENUM("Right Headphone Mux", hpr_enum);
-
 static const char *adc_mux_text[] = {
 	"ADC",
 	"DMIC",
@@ -1483,7 +1451,7 @@
 
 	ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
 
-	wm8994_update_class_w(codec);
+	wm_hubs_update_class_w(codec);
 
 	return ret;
 }
@@ -1577,7 +1545,7 @@
 	SOC_DAPM_ENUM("AIF3ADC Mux", wm8958_aif3adc_enum);
 
 static const char *mono_pcm_out_text[] = {
-	"None", "AIF2ADCL", "AIF2ADCR", 
+	"None", "AIF2ADCL", "AIF2ADCR",
 };
 
 static const struct soc_enum mono_pcm_out_enum =
@@ -1626,9 +1594,9 @@
 SND_SOC_DAPM_MIXER_E("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
 		     right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer),
 		     late_enable_ev, SND_SOC_DAPM_PRE_PMU),
-SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux,
+SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux,
 		   late_enable_ev, SND_SOC_DAPM_PRE_PMU),
-SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux,
+SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux,
 		   late_enable_ev, SND_SOC_DAPM_PRE_PMU),
 
 SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev)
@@ -1646,8 +1614,8 @@
 		   left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
 SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
 		   right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
-SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
-SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux),
 };
 
 static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = {
@@ -1691,7 +1659,8 @@
 		    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 
 SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event,
-		    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+		    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		    SND_SOC_DAPM_PRE_PMD),
 
 SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM, 3, 0, NULL, 0),
 SND_SOC_DAPM_SUPPLY("DSP2CLK", SND_SOC_NOPM, 2, 0, NULL, 0),
@@ -1787,6 +1756,7 @@
 };
 
 static const struct snd_soc_dapm_widget wm8958_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("AIF3", WM8994_POWER_MANAGEMENT_6, 5, 1, NULL, 0),
 SND_SOC_DAPM_MUX("Mono PCM Out Mux", SND_SOC_NOPM, 0, 0, &mono_pcm_out_mux),
 SND_SOC_DAPM_MUX("AIF2DACL Mux", SND_SOC_NOPM, 0, 0, &aif2dacl_src_mux),
 SND_SOC_DAPM_MUX("AIF2DACR Mux", SND_SOC_NOPM, 0, 0, &aif2dacr_src_mux),
@@ -2027,6 +1997,9 @@
 	{ "AIF2DACR Mux", "AIF2", "AIF2DAC Mux" },
 	{ "AIF2DACR Mux", "AIF3", "AIF3DACDAT" },
 
+	{ "AIF3DACDAT", NULL, "AIF3" },
+	{ "AIF3ADCDAT", NULL, "AIF3" },
+
 	{ "Mono PCM Out Mux", "AIF2ADCL", "AIF2ADCL" },
 	{ "Mono PCM Out Mux", "AIF2ADCR", "AIF2ADCR" },
 
@@ -2123,24 +2096,20 @@
 	struct wm8994 *control = wm8994->wm8994;
 	int reg_offset, ret;
 	struct fll_div fll;
-	u16 reg, aif1, aif2;
+	u16 reg, clk1, aif_reg, aif_src;
 	unsigned long timeout;
 	bool was_enabled;
 
-	aif1 = snd_soc_read(codec, WM8994_AIF1_CLOCKING_1)
-		& WM8994_AIF1CLK_ENA;
-
-	aif2 = snd_soc_read(codec, WM8994_AIF2_CLOCKING_1)
-		& WM8994_AIF2CLK_ENA;
-
 	switch (id) {
 	case WM8994_FLL1:
 		reg_offset = 0;
 		id = 0;
+		aif_src = 0x10;
 		break;
 	case WM8994_FLL2:
 		reg_offset = 0x20;
 		id = 1;
+		aif_src = 0x18;
 		break;
 	default:
 		return -EINVAL;
@@ -2161,6 +2130,10 @@
 	case WM8994_FLL_SRC_LRCLK:
 	case WM8994_FLL_SRC_BCLK:
 		break;
+	case WM8994_FLL_SRC_INTERNAL:
+		freq_in = 12000000;
+		freq_out = 12000000;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -2182,16 +2155,33 @@
 	if (ret < 0)
 		return ret;
 
-	/* Gate the AIF clocks while we reclock */
-	snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
-			    WM8994_AIF1CLK_ENA, 0);
-	snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1,
-			    WM8994_AIF2CLK_ENA, 0);
+	/* Make sure that we're not providing SYSCLK right now */
+	clk1 = snd_soc_read(codec, WM8994_CLOCKING_1);
+	if (clk1 & WM8994_SYSCLK_SRC)
+		aif_reg = WM8994_AIF2_CLOCKING_1;
+	else
+		aif_reg = WM8994_AIF1_CLOCKING_1;
+	reg = snd_soc_read(codec, aif_reg);
+
+	if ((reg & WM8994_AIF1CLK_ENA) &&
+	    (reg & WM8994_AIF1CLK_SRC_MASK) == aif_src) {
+		dev_err(codec->dev, "FLL%d is currently providing SYSCLK\n",
+			id + 1);
+		return -EBUSY;
+	}
 
 	/* We always need to disable the FLL while reconfiguring */
 	snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_1 + reg_offset,
 			    WM8994_FLL1_ENA, 0);
 
+	if (wm8994->fll_byp && src == WM8994_FLL_SRC_BCLK &&
+	    freq_in == freq_out && freq_out) {
+		dev_dbg(codec->dev, "Bypassing FLL%d\n", id + 1);
+		snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
+				    WM8958_FLL1_BYP, WM8958_FLL1_BYP);
+		goto out;
+	}
+
 	reg = (fll.outdiv << WM8994_FLL1_OUTDIV_SHIFT) |
 		(fll.fll_fratio << WM8994_FLL1_FRATIO_SHIFT);
 	snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_2 + reg_offset,
@@ -2203,11 +2193,14 @@
 
 	snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_4 + reg_offset,
 			    WM8994_FLL1_N_MASK,
-				    fll.n << WM8994_FLL1_N_SHIFT);
+			    fll.n << WM8994_FLL1_N_SHIFT);
 
 	snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
+			    WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP |
 			    WM8994_FLL1_REFCLK_DIV_MASK |
 			    WM8994_FLL1_REFCLK_SRC_MASK,
+			    ((src == WM8994_FLL_SRC_INTERNAL)
+			     << WM8994_FLL1_FRC_NCO_SHIFT) |
 			    (fll.clk_ref_div << WM8994_FLL1_REFCLK_DIV_SHIFT) |
 			    (src - 1));
 
@@ -2233,13 +2226,16 @@
 			}
 		}
 
+		reg = WM8994_FLL1_ENA;
+
 		if (fll.k)
-			reg = WM8994_FLL1_ENA | WM8994_FLL1_FRAC;
-		else
-			reg = WM8994_FLL1_ENA;
+			reg |= WM8994_FLL1_FRAC;
+		if (src == WM8994_FLL_SRC_INTERNAL)
+			reg |= WM8994_FLL1_OSC_ENA;
+
 		snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_1 + reg_offset,
-				    WM8994_FLL1_ENA | WM8994_FLL1_FRAC,
-				    reg);
+				    WM8994_FLL1_ENA | WM8994_FLL1_OSC_ENA |
+				    WM8994_FLL1_FRAC, reg);
 
 		if (wm8994->fll_locked_irq) {
 			timeout = wait_for_completion_timeout(&wm8994->fll_locked[id],
@@ -2268,16 +2264,11 @@
 		}
 	}
 
+out:
 	wm8994->fll[id].in = freq_in;
 	wm8994->fll[id].out = freq_out;
 	wm8994->fll[id].src = src;
 
-	/* Enable any gated AIF clocks */
-	snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
-			    WM8994_AIF1CLK_ENA, aif1);
-	snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1,
-			    WM8994_AIF2CLK_ENA, aif2);
-
 	configure_clock(codec);
 
 	return 0;
@@ -2345,7 +2336,7 @@
 
 	case WM8994_SYSCLK_OPCLK:
 		/* Special case - a division (times 10) is given and
-		 * no effect on main clocking. 
+		 * no effect on main clocking.
 		 */
 		if (freq) {
 			for (i = 0; i < ARRAY_SIZE(opclk_divs); i++)
@@ -2662,6 +2653,7 @@
 {
 	struct snd_soc_codec *codec = dai->codec;
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+	struct wm8994_pdata *pdata = wm8994->pdata;
 	int aif1_reg;
 	int aif2_reg;
 	int bclk_reg;
@@ -2707,7 +2699,7 @@
 		return -EINVAL;
 	}
 
-	bclk_rate = params_rate(params) * 4;
+	bclk_rate = params_rate(params);
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S16_LE:
 		bclk_rate *= 16;
@@ -2728,6 +2720,24 @@
 		return -EINVAL;
 	}
 
+	wm8994->channels[id] = params_channels(params);
+	if (pdata->max_channels_clocked[id] &&
+	    wm8994->channels[id] > pdata->max_channels_clocked[id]) {
+		dev_dbg(dai->dev, "Constraining channels to %d from %d\n",
+			pdata->max_channels_clocked[id], wm8994->channels[id]);
+		wm8994->channels[id] = pdata->max_channels_clocked[id];
+	}
+
+	switch (wm8994->channels[id]) {
+	case 1:
+	case 2:
+		bclk_rate *= 2;
+		break;
+	default:
+		bclk_rate *= 4;
+		break;
+	}
+
 	/* Try to find an appropriate sample rate; look for an exact match. */
 	for (i = 0; i < ARRAY_SIZE(srs); i++)
 		if (srs[i].rate == params_rate(params))
@@ -2740,7 +2750,7 @@
 	dev_dbg(dai->dev, "AIF%dCLK is %dHz, target BCLK %dHz\n",
 		dai->id, wm8994->aifclk[id], bclk_rate);
 
-	if (params_channels(params) == 1 &&
+	if (wm8994->channels[id] == 1 &&
 	    (snd_soc_read(codec, aif1_reg) & 0x18) == 0x18)
 		aif2 |= WM8994_AIF1_MONO;
 
@@ -2859,33 +2869,6 @@
 	return snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1);
 }
 
-static void wm8994_aif_shutdown(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
-{
-	struct snd_soc_codec *codec = dai->codec;
-	int rate_reg = 0;
-
-	switch (dai->id) {
-	case 1:
-		rate_reg = WM8994_AIF1_RATE;
-		break;
-	case 2:
-		rate_reg = WM8994_AIF2_RATE;
-		break;
-	default:
-		break;
-	}
-
-	/* If the DAI is idle then configure the divider tree for the
-	 * lowest output rate to save a little power if the clock is
-	 * still active (eg, because it is system clock).
-	 */
-	if (rate_reg && !dai->playback_active && !dai->capture_active)
-		snd_soc_update_bits(codec, rate_reg,
-				    WM8994_AIF1_SR_MASK |
-				    WM8994_AIF1CLK_RATE_MASK, 0x9);
-}
-
 static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -2927,10 +2910,6 @@
 		reg = WM8994_AIF2_MASTER_SLAVE;
 		mask = WM8994_AIF2_TRI;
 		break;
-	case 3:
-		reg = WM8994_POWER_MANAGEMENT_6;
-		mask = WM8994_AIF3_TRI;
-		break;
 	default:
 		return -EINVAL;
 	}
@@ -2967,7 +2946,6 @@
 	.set_sysclk	= wm8994_set_dai_sysclk,
 	.set_fmt	= wm8994_set_dai_fmt,
 	.hw_params	= wm8994_hw_params,
-	.shutdown	= wm8994_aif_shutdown,
 	.digital_mute	= wm8994_aif_mute,
 	.set_pll	= wm8994_set_fll,
 	.set_tristate	= wm8994_set_tristate,
@@ -2977,7 +2955,6 @@
 	.set_sysclk	= wm8994_set_dai_sysclk,
 	.set_fmt	= wm8994_set_dai_fmt,
 	.hw_params	= wm8994_hw_params,
-	.shutdown	= wm8994_aif_shutdown,
 	.digital_mute   = wm8994_aif_mute,
 	.set_pll	= wm8994_set_fll,
 	.set_tristate	= wm8994_set_tristate,
@@ -2985,7 +2962,6 @@
 
 static const struct snd_soc_dai_ops wm8994_aif3_dai_ops = {
 	.hw_params	= wm8994_aif3_hw_params,
-	.set_tristate	= wm8994_set_tristate,
 };
 
 static struct snd_soc_dai_driver wm8994_dai[] = {
@@ -3125,28 +3101,6 @@
 				 i + 1, ret);
 	}
 
-	switch (control->type) {
-	case WM8994:
-		if (wm8994->micdet[0].jack || wm8994->micdet[1].jack)
-			snd_soc_update_bits(codec, WM8994_MICBIAS,
-					    WM8994_MICD_ENA, WM8994_MICD_ENA);
-		break;
-	case WM1811:
-		if (wm8994->jackdet && wm8994->jack_cb) {
-			/* Restart from idle */
-			snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
-					    WM1811_JACKDET_MODE_MASK,
-					    WM1811_JACKDET_MODE_JACK);
-			break;
-		}
-		break;
-	case WM8958:
-		if (wm8994->jack_cb)
-			snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
-					    WM8958_MICD_ENA, WM8958_MICD_ENA);
-		break;
-	}
-
 	return 0;
 }
 #else
@@ -3156,7 +3110,7 @@
 
 static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994)
 {
-	struct snd_soc_codec *codec = wm8994->codec;
+	struct snd_soc_codec *codec = wm8994->hubs.codec;
 	struct wm8994_pdata *pdata = wm8994->pdata;
 	struct snd_kcontrol_new controls[] = {
 		SOC_ENUM_EXT("AIF1.1 EQ Mode",
@@ -3193,14 +3147,14 @@
 
 		/* Expand the array... */
 		t = krealloc(wm8994->retune_mobile_texts,
-			     sizeof(char *) * 
+			     sizeof(char *) *
 			     (wm8994->num_retune_mobile_texts + 1),
 			     GFP_KERNEL);
 		if (t == NULL)
 			continue;
 
 		/* ...store the new entry... */
-		t[wm8994->num_retune_mobile_texts] = 
+		t[wm8994->num_retune_mobile_texts] =
 			pdata->retune_mobile_cfgs[i].name;
 
 		/* ...and remember the new version. */
@@ -3214,16 +3168,16 @@
 	wm8994->retune_mobile_enum.max = wm8994->num_retune_mobile_texts;
 	wm8994->retune_mobile_enum.texts = wm8994->retune_mobile_texts;
 
-	ret = snd_soc_add_codec_controls(wm8994->codec, controls,
+	ret = snd_soc_add_codec_controls(wm8994->hubs.codec, controls,
 				   ARRAY_SIZE(controls));
 	if (ret != 0)
-		dev_err(wm8994->codec->dev,
+		dev_err(wm8994->hubs.codec->dev,
 			"Failed to add ReTune Mobile controls: %d\n", ret);
 }
 
 static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
 {
-	struct snd_soc_codec *codec = wm8994->codec;
+	struct snd_soc_codec *codec = wm8994->hubs.codec;
 	struct wm8994_pdata *pdata = wm8994->pdata;
 	int ret, i;
 
@@ -3252,10 +3206,10 @@
 		};
 
 		/* We need an array of texts for the enum API */
-		wm8994->drc_texts = devm_kzalloc(wm8994->codec->dev,
+		wm8994->drc_texts = devm_kzalloc(wm8994->hubs.codec->dev,
 			    sizeof(char *) * pdata->num_drc_cfgs, GFP_KERNEL);
 		if (!wm8994->drc_texts) {
-			dev_err(wm8994->codec->dev,
+			dev_err(wm8994->hubs.codec->dev,
 				"Failed to allocate %d DRC config texts\n",
 				pdata->num_drc_cfgs);
 			return;
@@ -3267,23 +3221,28 @@
 		wm8994->drc_enum.max = pdata->num_drc_cfgs;
 		wm8994->drc_enum.texts = wm8994->drc_texts;
 
-		ret = snd_soc_add_codec_controls(wm8994->codec, controls,
+		ret = snd_soc_add_codec_controls(wm8994->hubs.codec, controls,
 					   ARRAY_SIZE(controls));
-		if (ret != 0)
-			dev_err(wm8994->codec->dev,
-				"Failed to add DRC mode controls: %d\n", ret);
-
 		for (i = 0; i < WM8994_NUM_DRC; i++)
 			wm8994_set_drc(codec, i);
+	} else {
+		ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
+						 wm8994_drc_controls,
+						 ARRAY_SIZE(wm8994_drc_controls));
 	}
 
+	if (ret != 0)
+		dev_err(wm8994->hubs.codec->dev,
+			"Failed to add DRC mode controls: %d\n", ret);
+
+
 	dev_dbg(codec->dev, "%d ReTune Mobile configurations\n",
 		pdata->num_retune_mobile_cfgs);
 
 	if (pdata->num_retune_mobile_cfgs)
 		wm8994_handle_retune_mobile_pdata(wm8994);
 	else
-		snd_soc_add_codec_controls(wm8994->codec, wm8994_eq_controls,
+		snd_soc_add_codec_controls(wm8994->hubs.codec, wm8994_eq_controls,
 				     ARRAY_SIZE(wm8994_eq_controls));
 
 	for (i = 0; i < ARRAY_SIZE(pdata->micbias); i++) {
@@ -3365,31 +3324,40 @@
 
 	snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, reg);
 
+	/* enable MICDET and MICSHRT deboune */
+	snd_soc_update_bits(codec, WM8994_IRQ_DEBOUNCE,
+			    WM8994_MIC1_DET_DB_MASK | WM8994_MIC1_SHRT_DB_MASK |
+			    WM8994_MIC2_DET_DB_MASK | WM8994_MIC2_SHRT_DB_MASK,
+			    WM8994_MIC1_DET_DB | WM8994_MIC1_SHRT_DB);
+
 	snd_soc_dapm_sync(&codec->dapm);
 
 	return 0;
 }
 EXPORT_SYMBOL_GPL(wm8994_mic_detect);
 
-static irqreturn_t wm8994_mic_irq(int irq, void *data)
+static void wm8994_mic_work(struct work_struct *work)
 {
-	struct wm8994_priv *priv = data;
-	struct snd_soc_codec *codec = priv->codec;
-	int reg;
+	struct wm8994_priv *priv = container_of(work,
+						struct wm8994_priv,
+						mic_work.work);
+	struct regmap *regmap = priv->wm8994->regmap;
+	struct device *dev = priv->wm8994->dev;
+	unsigned int reg;
+	int ret;
 	int report;
 
-#ifndef CONFIG_SND_SOC_WM8994_MODULE
-	trace_snd_soc_jack_irq(dev_name(codec->dev));
-#endif
+	pm_runtime_get_sync(dev);
 
-	reg = snd_soc_read(codec, WM8994_INTERRUPT_RAW_STATUS_2);
-	if (reg < 0) {
-		dev_err(codec->dev, "Failed to read microphone status: %d\n",
-			reg);
-		return IRQ_HANDLED;
+	ret = regmap_read(regmap, WM8994_INTERRUPT_RAW_STATUS_2, &reg);
+	if (ret < 0) {
+		dev_err(dev, "Failed to read microphone status: %d\n",
+			ret);
+		pm_runtime_put(dev);
+		return;
 	}
 
-	dev_dbg(codec->dev, "Microphone status: %x\n", reg);
+	dev_dbg(dev, "Microphone status: %x\n", reg);
 
 	report = 0;
 	if (reg & WM8994_MIC1_DET_STS) {
@@ -3429,41 +3397,113 @@
 	snd_soc_jack_report(priv->micdet[1].jack, report,
 			    SND_JACK_HEADSET | SND_JACK_BTN_0);
 
+	pm_runtime_put(dev);
+}
+
+static irqreturn_t wm8994_mic_irq(int irq, void *data)
+{
+	struct wm8994_priv *priv = data;
+	struct snd_soc_codec *codec = priv->hubs.codec;
+
+#ifndef CONFIG_SND_SOC_WM8994_MODULE
+	trace_snd_soc_jack_irq(dev_name(codec->dev));
+#endif
+
+	pm_wakeup_event(codec->dev, 300);
+
+	schedule_delayed_work(&priv->mic_work, msecs_to_jiffies(250));
+
 	return IRQ_HANDLED;
 }
 
-/* Default microphone detection handler for WM8958 - the user can
- * override this if they wish.
- */
-static void wm8958_default_micdet(u16 status, void *data)
+static void wm1811_micd_stop(struct snd_soc_codec *codec)
 {
-	struct snd_soc_codec *codec = data;
+	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+	if (!wm8994->jackdet)
+		return;
+
+	snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, 0);
+
+	wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_JACK);
+
+	if (wm8994->pdata->jd_ext_cap)
+		snd_soc_dapm_disable_pin(&codec->dapm,
+					 "MICBIAS2");
+}
+
+static void wm8958_button_det(struct snd_soc_codec *codec, u16 status)
+{
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 	int report;
 
-	dev_dbg(codec->dev, "MICDET %x\n", status);
+	report = 0;
+	if (status & 0x4)
+		report |= SND_JACK_BTN_0;
+
+	if (status & 0x8)
+		report |= SND_JACK_BTN_1;
+
+	if (status & 0x10)
+		report |= SND_JACK_BTN_2;
+
+	if (status & 0x20)
+		report |= SND_JACK_BTN_3;
+
+	if (status & 0x40)
+		report |= SND_JACK_BTN_4;
+
+	if (status & 0x80)
+		report |= SND_JACK_BTN_5;
+
+	snd_soc_jack_report(wm8994->micdet[0].jack, report,
+			    wm8994->btn_mask);
+}
+
+static void wm8958_open_circuit_work(struct work_struct *work)
+{
+	struct wm8994_priv *wm8994 = container_of(work,
+						  struct wm8994_priv,
+						  open_circuit_work.work);
+	struct device *dev = wm8994->wm8994->dev;
+
+	mutex_lock(&wm8994->accdet_lock);
+
+	dev_dbg(dev, "Reporting open circuit\n");
+
+	wm8994->jack_mic = false;
+	wm8994->mic_detecting = true;
+
+	wm1811_micd_stop(wm8994->hubs.codec);
+
+	wm8958_micd_set_rate(wm8994->hubs.codec);
+
+	snd_soc_jack_report(wm8994->micdet[0].jack, 0,
+			    wm8994->btn_mask |
+			    SND_JACK_HEADSET);
+
+	mutex_unlock(&wm8994->accdet_lock);
+}
+
+void wm8958_mic_id(void *data, u16 status)
+{
+	struct snd_soc_codec *codec = data;
+	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
 	/* Either nothing present or just starting detection */
 	if (!(status & WM8958_MICD_STS)) {
-		if (!wm8994->jackdet) {
-			/* If nothing present then clear our statuses */
-			dev_dbg(codec->dev, "Detected open circuit\n");
-			wm8994->jack_mic = false;
-			wm8994->mic_detecting = true;
+		/* If nothing present then clear our statuses */
+		dev_dbg(codec->dev, "Detected open circuit\n");
 
-			wm8958_micd_set_rate(codec);
-
-			snd_soc_jack_report(wm8994->micdet[0].jack, 0,
-					    wm8994->btn_mask |
-					     SND_JACK_HEADSET);
-		}
+		schedule_delayed_work(&wm8994->open_circuit_work,
+				      msecs_to_jiffies(2000));
 		return;
 	}
 
 	/* If the measurement is showing a high impedence we've got a
 	 * microphone.
 	 */
-	if (wm8994->mic_detecting && (status & 0x600)) {
+	if (status & 0x600) {
 		dev_dbg(codec->dev, "Detected microphone\n");
 
 		wm8994->mic_detecting = false;
@@ -3476,7 +3516,7 @@
 	}
 
 
-	if (wm8994->mic_detecting && status & 0xfc) {
+	if (status & 0xfc) {
 		dev_dbg(codec->dev, "Detected headphone\n");
 		wm8994->mic_detecting = false;
 
@@ -3486,66 +3526,72 @@
 				    SND_JACK_HEADSET);
 
 		/* If we have jackdet that will detect removal */
-		if (wm8994->jackdet) {
-			mutex_lock(&wm8994->accdet_lock);
+		mutex_lock(&wm8994->accdet_lock);
+		wm1811_micd_stop(codec);
+		mutex_unlock(&wm8994->accdet_lock);
+	}
+}
 
-			snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
-					    WM8958_MICD_ENA, 0);
+/* Deferred mic detection to allow for extra settling time */
+static void wm1811_mic_work(struct work_struct *work)
+{
+	struct wm8994_priv *wm8994 = container_of(work, struct wm8994_priv,
+						  mic_work.work);
+	struct snd_soc_codec *codec = wm8994->hubs.codec;
 
-			wm1811_jackdet_set_mode(codec,
-						WM1811_JACKDET_MODE_JACK);
+	pm_runtime_get_sync(codec->dev);
 
-			mutex_unlock(&wm8994->accdet_lock);
+	mutex_lock(&codec->mutex);
+	/* If required for an external cap force MICBIAS on */
+	if (wm8994->pdata->jd_ext_cap) {
+		snd_soc_dapm_force_enable_pin(&codec->dapm,
+					      "MICBIAS2");
+		snd_soc_dapm_sync(&codec->dapm);
+	}
+	mutex_unlock(&codec->mutex);
 
-			if (wm8994->pdata->jd_ext_cap) {
-				mutex_lock(&codec->mutex);
-				snd_soc_dapm_disable_pin(&codec->dapm,
-							 "MICBIAS2");
-				snd_soc_dapm_sync(&codec->dapm);
-				mutex_unlock(&codec->mutex);
-			}
-		}
+	mutex_lock(&wm8994->accdet_lock);
+
+	dev_dbg(codec->dev, "Starting mic detection\n");
+
+	/* Use a user-supplied callback if we have one */
+	if (wm8994->micd_cb) {
+		wm8994->micd_cb(wm8994->micd_cb_data);
+	} else {
+		/*
+		 * Start off measument of microphone impedence to find out
+		 * what's actually there.
+		 */
+		wm8994->mic_detecting = true;
+		wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_MIC);
+
+		snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+				    WM8958_MICD_ENA, WM8958_MICD_ENA);
 	}
 
-	/* Report short circuit as a button */
-	if (wm8994->jack_mic) {
-		report = 0;
-		if (status & 0x4)
-			report |= SND_JACK_BTN_0;
+	mutex_unlock(&wm8994->accdet_lock);
 
-		if (status & 0x8)
-			report |= SND_JACK_BTN_1;
-
-		if (status & 0x10)
-			report |= SND_JACK_BTN_2;
-
-		if (status & 0x20)
-			report |= SND_JACK_BTN_3;
-
-		if (status & 0x40)
-			report |= SND_JACK_BTN_4;
-
-		if (status & 0x80)
-			report |= SND_JACK_BTN_5;
-
-		snd_soc_jack_report(wm8994->micdet[0].jack, report,
-				    wm8994->btn_mask);
-	}
+	pm_runtime_put(codec->dev);
 }
 
 static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
 {
 	struct wm8994_priv *wm8994 = data;
-	struct snd_soc_codec *codec = wm8994->codec;
-	int reg;
+	struct snd_soc_codec *codec = wm8994->hubs.codec;
+	int reg, delay;
 	bool present;
 
+	cancel_delayed_work_sync(&wm8994->mic_work);
+
+	pm_runtime_get_sync(codec->dev);
+
 	mutex_lock(&wm8994->accdet_lock);
 
 	reg = snd_soc_read(codec, WM1811_JACKDET_CTRL);
 	if (reg < 0) {
 		dev_err(codec->dev, "Failed to read jack status: %d\n", reg);
 		mutex_unlock(&wm8994->accdet_lock);
+		pm_runtime_put(codec->dev);
 		return IRQ_NONE;
 	}
 
@@ -3556,6 +3602,8 @@
 	if (present) {
 		dev_dbg(codec->dev, "Jack detected\n");
 
+		wm8958_micd_set_rate(codec);
+
 		snd_soc_update_bits(codec, WM8958_MICBIAS2,
 				    WM8958_MICB2_DISCH, 0);
 
@@ -3563,15 +3611,9 @@
 		snd_soc_update_bits(codec, WM1811_JACKDET_CTRL,
 				    WM1811_JACKDET_DB, 0);
 
-		/*
-		 * Start off measument of microphone impedence to find
-		 * out what's actually there.
-		 */
-		wm8994->mic_detecting = true;
-		wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_MIC);
-
-		snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
-				    WM8958_MICD_ENA, WM8958_MICD_ENA);
+		delay = wm8994->pdata->micdet_delay;
+		schedule_delayed_work(&wm8994->mic_work,
+				      msecs_to_jiffies(delay));
 	} else {
 		dev_dbg(codec->dev, "Jack not detected\n");
 
@@ -3591,19 +3633,11 @@
 
 	mutex_unlock(&wm8994->accdet_lock);
 
-	/* If required for an external cap force MICBIAS on */
-	if (wm8994->pdata->jd_ext_cap) {
-		mutex_lock(&codec->mutex);
-
-		if (present)
-			snd_soc_dapm_force_enable_pin(&codec->dapm,
-						      "MICBIAS2");
-		else
-			snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS2");
-
-		snd_soc_dapm_sync(&codec->dapm);
-		mutex_unlock(&codec->mutex);
-	}
+	mutex_lock(&codec->mutex);
+	/* Turn off MICBIAS if it was on for an external cap */
+	if (wm8994->pdata->jd_ext_cap && !present)
+		snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS2");
+	mutex_unlock(&codec->mutex);
 
 	if (present)
 		snd_soc_jack_report(wm8994->micdet[0].jack,
@@ -3613,9 +3647,22 @@
 				    SND_JACK_MECHANICAL | SND_JACK_HEADSET |
 				    wm8994->btn_mask);
 
+	/* Since we only report deltas force an update, ensures we
+	 * avoid bootstrapping issues with the core. */
+	snd_soc_jack_report(wm8994->micdet[0].jack, 0, 0);
+
+	pm_runtime_put(codec->dev);
 	return IRQ_HANDLED;
 }
 
+static void wm1811_jackdet_bootstrap(struct work_struct *work)
+{
+	struct wm8994_priv *wm8994 = container_of(work,
+						struct wm8994_priv,
+						jackdet_bootstrap.work);
+	wm1811_jackdet_irq(0, wm8994);
+}
+
 /**
  * wm8958_mic_detect - Enable microphone detection via the WM8958 IRQ
  *
@@ -3633,7 +3680,8 @@
  * detection algorithm.
  */
 int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
-		      wm8958_micdet_cb cb, void *cb_data)
+		      wm1811_micdet_cb det_cb, void *det_cb_data,
+		      wm1811_mic_id_cb id_cb, void *id_cb_data)
 {
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 	struct wm8994 *control = wm8994->wm8994;
@@ -3648,21 +3696,26 @@
 	}
 
 	if (jack) {
-		if (!cb) {
-			dev_dbg(codec->dev, "Using default micdet callback\n");
-			cb = wm8958_default_micdet;
-			cb_data = codec;
-		}
-
 		snd_soc_dapm_force_enable_pin(&codec->dapm, "CLK_SYS");
 		snd_soc_dapm_sync(&codec->dapm);
 
 		wm8994->micdet[0].jack = jack;
-		wm8994->jack_cb = cb;
-		wm8994->jack_cb_data = cb_data;
 
-		wm8994->mic_detecting = true;
-		wm8994->jack_mic = false;
+		if (det_cb) {
+			wm8994->micd_cb = det_cb;
+			wm8994->micd_cb_data = det_cb_data;
+		} else {
+			wm8994->mic_detecting = true;
+			wm8994->jack_mic = false;
+		}
+
+		if (id_cb) {
+			wm8994->mic_id_cb = id_cb;
+			wm8994->mic_id_cb_data = id_cb_data;
+		} else {
+			wm8994->mic_id_cb = wm8958_mic_id;
+			wm8994->mic_id_cb_data = codec;
+		}
 
 		wm8958_micd_set_rate(codec);
 
@@ -3686,6 +3739,10 @@
 		 * otherwise jump straight to microphone detection.
 		 */
 		if (wm8994->jackdet) {
+			/* Disable debounce for the initial detect */
+			snd_soc_update_bits(codec, WM1811_JACKDET_CTRL,
+					    WM1811_JACKDET_DB, 0);
+
 			snd_soc_update_bits(codec, WM8958_MICBIAS2,
 					    WM8958_MICB2_DISCH,
 					    WM8958_MICB2_DISCH);
@@ -3713,8 +3770,8 @@
 static irqreturn_t wm8958_mic_irq(int irq, void *data)
 {
 	struct wm8994_priv *wm8994 = data;
-	struct snd_soc_codec *codec = wm8994->codec;
-	int reg, count;
+	struct snd_soc_codec *codec = wm8994->hubs.codec;
+	int reg, count, ret;
 
 	/*
 	 * Jack detection may have detected a removal simulataneously
@@ -3724,6 +3781,10 @@
 	if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA))
 		return IRQ_HANDLED;
 
+	cancel_delayed_work_sync(&wm8994->open_circuit_work);
+
+	pm_runtime_get_sync(codec->dev);
+
 	/* We may occasionally read a detection without an impedence
 	 * range being provided - if that happens loop again.
 	 */
@@ -3734,6 +3795,7 @@
 			dev_err(codec->dev,
 				"Failed to read mic detect status: %d\n",
 				reg);
+			pm_runtime_put(codec->dev);
 			return IRQ_NONE;
 		}
 
@@ -3755,12 +3817,25 @@
 	trace_snd_soc_jack_irq(dev_name(codec->dev));
 #endif
 
-	if (wm8994->jack_cb)
-		wm8994->jack_cb(reg, wm8994->jack_cb_data);
+	/* Avoid a transient report when the accessory is being removed */
+	if (wm8994->jackdet) {
+		ret = snd_soc_read(codec, WM1811_JACKDET_CTRL);
+		if (ret < 0) {
+			dev_err(codec->dev, "Failed to read jack status: %d\n",
+				ret);
+		} else if (!(ret & WM1811_JACKDET_LVL)) {
+			dev_dbg(codec->dev, "Ignoring removed jack\n");
+			return IRQ_HANDLED;
+		}
+	}
+
+	if (wm8994->mic_detecting)
+		wm8994->mic_id_cb(wm8994->mic_id_cb_data, reg);
 	else
-		dev_warn(codec->dev, "Accessory detection with no callback\n");
+		wm8958_button_det(codec, reg);
 
 out:
+	pm_runtime_put(codec->dev);
 	return IRQ_HANDLED;
 }
 
@@ -3799,14 +3874,27 @@
 	unsigned int reg;
 	int ret, i;
 
-	wm8994->codec = codec;
+	wm8994->hubs.codec = codec;
 	codec->control_data = control->regmap;
 
 	snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_REGMAP);
 
-	wm8994->codec = codec;
-
 	mutex_init(&wm8994->accdet_lock);
+	INIT_DELAYED_WORK(&wm8994->jackdet_bootstrap,
+			  wm1811_jackdet_bootstrap);
+	INIT_DELAYED_WORK(&wm8994->open_circuit_work,
+			  wm8958_open_circuit_work);
+
+	switch (control->type) {
+	case WM8994:
+		INIT_DELAYED_WORK(&wm8994->mic_work, wm8994_mic_work);
+		break;
+	case WM1811:
+		INIT_DELAYED_WORK(&wm8994->mic_work, wm1811_mic_work);
+		break;
+	default:
+		break;
+	}
 
 	for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
 		init_completion(&wm8994->fll_locked[i]);
@@ -3850,22 +3938,34 @@
 	case WM8958:
 		wm8994->hubs.dcs_readback_mode = 1;
 		wm8994->hubs.hp_startup_mode = 1;
+
+		switch (wm8994->revision) {
+		case 0:
+			break;
+		default:
+			wm8994->fll_byp = true;
+			break;
+		}
 		break;
 
 	case WM1811:
 		wm8994->hubs.dcs_readback_mode = 2;
 		wm8994->hubs.no_series_update = 1;
 		wm8994->hubs.hp_startup_mode = 1;
-		wm8994->hubs.no_cache_class_w = true;
+		wm8994->hubs.no_cache_dac_hp_direct = true;
+		wm8994->fll_byp = true;
 
-		switch (wm8994->revision) {
+		switch (control->cust_id) {
 		case 0:
-		case 1:
 		case 2:
-		case 3:
 			wm8994->hubs.dcs_codes_l = -9;
 			wm8994->hubs.dcs_codes_r = -7;
 			break;
+		case 1:
+		case 3:
+			wm8994->hubs.dcs_codes_l = -8;
+			wm8994->hubs.dcs_codes_r = -7;
+			break;
 		default:
 			break;
 		}
@@ -3950,7 +4050,7 @@
 
 	switch (control->type) {
 	case WM1811:
-		if (wm8994->revision > 1) {
+		if (control->cust_id > 1 || wm8994->revision > 1) {
 			ret = wm8994_request_irq(wm8994->wm8994,
 						 WM8994_IRQ_GPIO(6),
 						 wm1811_jackdet_irq, "JACKDET",
@@ -4049,7 +4149,8 @@
 		break;
 	}
 
-	wm8994_update_class_w(codec);
+	wm8994->hubs.check_class_w_digital = wm8994_check_class_w_digital;
+	wm_hubs_update_class_w(codec);
 
 	wm8994_handle_pdata(wm8994);
 
@@ -4114,7 +4215,6 @@
 					  ARRAY_SIZE(wm8994_dac_widgets));
 		break;
 	}
-		
 
 	wm_hubs_add_analogue_routes(codec, 0, 0);
 	snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
@@ -4136,6 +4236,8 @@
 		break;
 	case WM8958:
 		if (wm8994->revision < 1) {
+			snd_soc_dapm_add_routes(dapm, wm8994_intercon,
+						ARRAY_SIZE(wm8994_intercon));
 			snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon,
 						ARRAY_SIZE(wm8994_revd_intercon));
 			snd_soc_dapm_add_routes(dapm, wm8994_lateclk_revd_intercon,
@@ -4179,7 +4281,7 @@
 	return ret;
 }
 
-static int  wm8994_codec_remove(struct snd_soc_codec *codec)
+static int wm8994_codec_remove(struct snd_soc_codec *codec)
 {
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 	struct wm8994 *control = wm8994->wm8994;
@@ -4220,14 +4322,10 @@
 			free_irq(wm8994->micdet_irq, wm8994);
 		break;
 	}
-	if (wm8994->mbc)
-		release_firmware(wm8994->mbc);
-	if (wm8994->mbc_vss)
-		release_firmware(wm8994->mbc_vss);
-	if (wm8994->enh_eq)
-		release_firmware(wm8994->enh_eq);
+	release_firmware(wm8994->mbc);
+	release_firmware(wm8994->mbc_vss);
+	release_firmware(wm8994->enh_eq);
 	kfree(wm8994->retune_mobile_texts);
-
 	return 0;
 }
 
@@ -4280,7 +4378,7 @@
 {
 	struct wm8994_priv *wm8994 = dev_get_drvdata(dev);
 
-	if (wm8994->jackdet && wm8994->jack_cb)
+	if (wm8994->jackdet && wm8994->jackdet_mode)
 		regmap_update_bits(wm8994->wm8994->regmap, WM8994_ANTIPOP_2,
 				   WM1811_JACKDET_MODE_MASK,
 				   WM1811_JACKDET_MODE_AUDIO);
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index c724112..b2b83e7 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -12,6 +12,7 @@
 #include <sound/soc.h>
 #include <linux/firmware.h>
 #include <linux/completion.h>
+#include <linux/workqueue.h>
 
 #include "wm_hubs.h"
 
@@ -27,22 +28,25 @@
 #define WM8994_FLL1 1
 #define WM8994_FLL2 2
 
-#define WM8994_FLL_SRC_MCLK1  1
-#define WM8994_FLL_SRC_MCLK2  2
-#define WM8994_FLL_SRC_LRCLK  3
-#define WM8994_FLL_SRC_BCLK   4
+#define WM8994_FLL_SRC_MCLK1    1
+#define WM8994_FLL_SRC_MCLK2    2
+#define WM8994_FLL_SRC_LRCLK    3
+#define WM8994_FLL_SRC_BCLK     4
+#define WM8994_FLL_SRC_INTERNAL 5
 
 enum wm8994_vmid_mode {
 	WM8994_VMID_NORMAL,
 	WM8994_VMID_FORCE,
 };
 
-typedef void (*wm8958_micdet_cb)(u16 status, void *data);
+typedef void (*wm1811_micdet_cb)(void *data);
+typedef void (*wm1811_mic_id_cb)(void *data, u16 status);
 
 int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
 		      int micbias);
 int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
-		      wm8958_micdet_cb cb, void *cb_data);
+		      wm1811_micdet_cb cb, void *det_cb_data,
+		      wm1811_mic_id_cb id_cb, void *id_cb_data);
 
 int wm8994_vmid_mode(struct snd_soc_codec *codec, enum wm8994_vmid_mode mode);
 
@@ -51,6 +55,8 @@
 
 void wm8958_dsp2_init(struct snd_soc_codec *codec);
 
+void wm8958_mic_id(void *data, u16 status);
+
 struct wm8994_micdet {
 	struct snd_soc_jack *jack;
 	bool detecting;
@@ -71,14 +77,16 @@
 struct wm8994_priv {
 	struct wm_hubs_data hubs;
 	struct wm8994 *wm8994;
-	struct snd_soc_codec *codec;
 	int sysclk[2];
 	int sysclk_rate[2];
 	int mclk[2];
 	int aifclk[2];
+	int channels[2];
 	struct wm8994_fll_config fll[2], fll_suspend[2];
 	struct completion fll_locked[2];
 	bool fll_locked_irq;
+	bool fll_byp;
+	bool clk_has_run;
 
 	int vmid_refcount;
 	int active_refcount;
@@ -126,15 +134,20 @@
 
 	struct mutex accdet_lock;
 	struct wm8994_micdet micdet[2];
+	struct delayed_work mic_work;
+	struct delayed_work open_circuit_work;
 	bool mic_detecting;
 	bool jack_mic;
 	int btn_mask;
 	bool jackdet;
 	int jackdet_mode;
+	struct delayed_work jackdet_bootstrap;
 
-	wm8958_micdet_cb jack_cb;
-	void *jack_cb_data;
 	int micdet_irq;
+	wm1811_micdet_cb micd_cb;
+	void *micd_cb_data;
+	wm1811_mic_id_cb mic_id_cb;
+	void *mic_id_cb_data;
 
 	int revision;
 	struct wm8994_pdata *pdata;
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index 6c028c4..b7dea8e 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -109,14 +109,146 @@
 }
 EXPORT_SYMBOL_GPL(wm_hubs_dcs_done);
 
+static bool wm_hubs_dac_hp_direct(struct snd_soc_codec *codec)
+{
+	int reg;
+
+	/* If we're going via the mixer we'll need to do additional checks */
+	reg = snd_soc_read(codec, WM8993_OUTPUT_MIXER1);
+	if (!(reg & WM8993_DACL_TO_HPOUT1L)) {
+		if (reg & ~WM8993_DACL_TO_MIXOUTL) {
+			dev_vdbg(codec->dev, "Analogue paths connected: %x\n",
+				 reg & ~WM8993_DACL_TO_HPOUT1L);
+			return false;
+		} else {
+			dev_vdbg(codec->dev, "HPL connected to mixer\n");
+		}
+	} else {
+		dev_vdbg(codec->dev, "HPL connected to DAC\n");
+	}
+
+	reg = snd_soc_read(codec, WM8993_OUTPUT_MIXER2);
+	if (!(reg & WM8993_DACR_TO_HPOUT1R)) {
+		if (reg & ~WM8993_DACR_TO_MIXOUTR) {
+			dev_vdbg(codec->dev, "Analogue paths connected: %x\n",
+				 reg & ~WM8993_DACR_TO_HPOUT1R);
+			return false;
+		} else {
+			dev_vdbg(codec->dev, "HPR connected to mixer\n");
+		}
+	} else {
+		dev_vdbg(codec->dev, "HPR connected to DAC\n");
+	}
+
+	return true;
+}
+
+struct wm_hubs_dcs_cache {
+	struct list_head list;
+	unsigned int left;
+	unsigned int right;
+	u16 dcs_cfg;
+};
+
+static bool wm_hubs_dcs_cache_get(struct snd_soc_codec *codec,
+				  struct wm_hubs_dcs_cache **entry)
+{
+	struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+	struct wm_hubs_dcs_cache *cache;
+	unsigned int left, right;
+
+	left = snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME);
+	left &= WM8993_HPOUT1L_VOL_MASK;
+
+	right = snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME);
+	right &= WM8993_HPOUT1R_VOL_MASK;
+
+	list_for_each_entry(cache, &hubs->dcs_cache, list) {
+		if (cache->left != left || cache->right != right)
+			continue;
+
+		*entry = cache;
+		return true;
+	}
+
+	return false;
+}
+
+static void wm_hubs_dcs_cache_set(struct snd_soc_codec *codec, u16 dcs_cfg)
+{
+	struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+	struct wm_hubs_dcs_cache *cache;
+
+	if (hubs->no_cache_dac_hp_direct)
+		return;
+
+	cache = devm_kzalloc(codec->dev, sizeof(*cache), GFP_KERNEL);
+	if (!cache) {
+		dev_err(codec->dev, "Failed to allocate DCS cache entry\n");
+		return;
+	}
+
+	cache->left = snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME);
+	cache->left &= WM8993_HPOUT1L_VOL_MASK;
+
+	cache->right = snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME);
+	cache->right &= WM8993_HPOUT1R_VOL_MASK;
+
+	cache->dcs_cfg = dcs_cfg;
+
+	list_add_tail(&cache->list, &hubs->dcs_cache);
+}
+
+static void wm_hubs_read_dc_servo(struct snd_soc_codec *codec,
+				  u16 *reg_l, u16 *reg_r)
+{
+	struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+	u16 dcs_reg, reg;
+
+	switch (hubs->dcs_readback_mode) {
+	case 2:
+		dcs_reg = WM8994_DC_SERVO_4E;
+		break;
+	case 1:
+		dcs_reg = WM8994_DC_SERVO_READBACK;
+		break;
+	default:
+		dcs_reg = WM8993_DC_SERVO_3;
+		break;
+	}
+
+	/* Different chips in the family support different readback
+	 * methods.
+	 */
+	switch (hubs->dcs_readback_mode) {
+	case 0:
+		*reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1)
+			& WM8993_DCS_INTEG_CHAN_0_MASK;
+		*reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
+			& WM8993_DCS_INTEG_CHAN_1_MASK;
+		break;
+	case 2:
+	case 1:
+		reg = snd_soc_read(codec, dcs_reg);
+		*reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
+			>> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
+		*reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
+		break;
+	default:
+		WARN(1, "Unknown DCS readback method\n");
+		return;
+	}
+}
+
 /*
  * Startup calibration of the DC servo
  */
-static void calibrate_dc_servo(struct snd_soc_codec *codec)
+static void enable_dc_servo(struct snd_soc_codec *codec)
 {
 	struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+	struct wm_hubs_dcs_cache *cache;
 	s8 offset;
-	u16 reg, reg_l, reg_r, dcs_cfg, dcs_reg;
+	u16 reg_l, reg_r, dcs_cfg, dcs_reg;
 
 	switch (hubs->dcs_readback_mode) {
 	case 2:
@@ -129,10 +261,11 @@
 
 	/* If we're using a digital only path and have a previously
 	 * callibrated DC servo offset stored then use that. */
-	if (hubs->class_w && hubs->class_w_dcs) {
-		dev_dbg(codec->dev, "Using cached DC servo offset %x\n",
-			hubs->class_w_dcs);
-		snd_soc_write(codec, dcs_reg, hubs->class_w_dcs);
+	if (wm_hubs_dac_hp_direct(codec) &&
+	    wm_hubs_dcs_cache_get(codec, &cache)) {
+		dev_dbg(codec->dev, "Using cached DCS offset %x for %d,%d\n",
+			cache->dcs_cfg, cache->left, cache->right);
+		snd_soc_write(codec, dcs_reg, cache->dcs_cfg);
 		wait_for_dc_servo(codec,
 				  WM8993_DCS_TRIG_DAC_WR_0 |
 				  WM8993_DCS_TRIG_DAC_WR_1);
@@ -153,27 +286,7 @@
 				  WM8993_DCS_TRIG_STARTUP_1);
 	}
 
-	/* Different chips in the family support different readback
-	 * methods.
-	 */
-	switch (hubs->dcs_readback_mode) {
-	case 0:
-		reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1)
-			& WM8993_DCS_INTEG_CHAN_0_MASK;
-		reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
-			& WM8993_DCS_INTEG_CHAN_1_MASK;
-		break;
-	case 2:
-	case 1:
-		reg = snd_soc_read(codec, dcs_reg);
-		reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
-			>> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
-		reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
-		break;
-	default:
-		WARN(1, "Unknown DCS readback method\n");
-		return;
-	}
+	wm_hubs_read_dc_servo(codec, &reg_l, &reg_r);
 
 	dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r);
 
@@ -184,12 +297,16 @@
 			hubs->dcs_codes_l, hubs->dcs_codes_r);
 
 		/* HPOUT1R */
-		offset = reg_r;
+		offset = (s8)reg_r;
+		dev_dbg(codec->dev, "DCS right %d->%d\n", offset,
+			offset + hubs->dcs_codes_r);
 		offset += hubs->dcs_codes_r;
 		dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
 
 		/* HPOUT1L */
-		offset = reg_l;
+		offset = (s8)reg_l;
+		dev_dbg(codec->dev, "DCS left %d->%d\n", offset,
+			offset + hubs->dcs_codes_l);
 		offset += hubs->dcs_codes_l;
 		dcs_cfg |= (u8)offset;
 
@@ -207,8 +324,8 @@
 
 	/* Save the callibrated offset if we're in class W mode and
 	 * therefore don't have any analogue signal mixed in. */
-	if (hubs->class_w && !hubs->no_cache_class_w)
-		hubs->class_w_dcs = dcs_cfg;
+	if (wm_hubs_dac_hp_direct(codec))
+		wm_hubs_dcs_cache_set(codec, dcs_cfg);
 }
 
 /*
@@ -223,9 +340,6 @@
 
 	ret = snd_soc_put_volsw(kcontrol, ucontrol);
 
-	/* Updating the analogue gains invalidates the DC servo cache */
-	hubs->class_w_dcs = 0;
-
 	/* If we're applying an offset correction then updating the
 	 * callibration would be likely to introduce further offsets. */
 	if (hubs->dcs_codes_l || hubs->dcs_codes_r || hubs->no_series_update)
@@ -446,7 +560,7 @@
 		snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
 				    WM8993_DCS_TIMER_PERIOD_01_MASK, 0);
 
-		calibrate_dc_servo(codec);
+		enable_dc_servo(codec);
 
 		reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
 			WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
@@ -530,6 +644,91 @@
 	return 0;
 }
 
+void wm_hubs_update_class_w(struct snd_soc_codec *codec)
+{
+	struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+	int enable = WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ;
+
+	if (!wm_hubs_dac_hp_direct(codec))
+		enable = false;
+
+	if (hubs->check_class_w_digital && !hubs->check_class_w_digital(codec))
+		enable = false;
+
+	dev_vdbg(codec->dev, "Class W %s\n", enable ? "enabled" : "disabled");
+
+	snd_soc_update_bits(codec, WM8993_CLASS_W_0,
+			    WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ, enable);
+
+	snd_soc_write(codec, WM8993_LEFT_OUTPUT_VOLUME,
+		      snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME));
+	snd_soc_write(codec, WM8993_RIGHT_OUTPUT_VOLUME,
+		      snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME));
+}
+EXPORT_SYMBOL_GPL(wm_hubs_update_class_w);
+
+#define WM_HUBS_SINGLE_W(xname, reg, shift, max, invert) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_volsw, \
+	.get = snd_soc_dapm_get_volsw, .put = class_w_put_volsw, \
+	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+static int class_w_put_volsw(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = widget->codec;
+	int ret;
+
+	ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+	wm_hubs_update_class_w(codec);
+
+	return ret;
+}
+
+#define WM_HUBS_ENUM_W(xname, xenum) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_enum_double, \
+	.get = snd_soc_dapm_get_enum_double, \
+	.put = class_w_put_double, \
+	.private_value = (unsigned long)&xenum }
+
+static int class_w_put_double(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = widget->codec;
+	int ret;
+
+	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+	wm_hubs_update_class_w(codec);
+
+	return ret;
+}
+
+static const char *hp_mux_text[] = {
+	"Mixer",
+	"DAC",
+};
+
+static const struct soc_enum hpl_enum =
+	SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text);
+
+const struct snd_kcontrol_new wm_hubs_hpl_mux =
+	WM_HUBS_ENUM_W("Left Headphone Mux", hpl_enum);
+EXPORT_SYMBOL_GPL(wm_hubs_hpl_mux);
+
+static const struct soc_enum hpr_enum =
+	SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text);
+
+const struct snd_kcontrol_new wm_hubs_hpr_mux =
+	WM_HUBS_ENUM_W("Right Headphone Mux", hpr_enum);
+EXPORT_SYMBOL_GPL(wm_hubs_hpr_mux);
+
 static const struct snd_kcontrol_new in1l_pga[] = {
 SOC_DAPM_SINGLE("IN1LP Switch", WM8993_INPUT_MIXER2, 5, 1, 0),
 SOC_DAPM_SINGLE("IN1LN Switch", WM8993_INPUT_MIXER2, 4, 1, 0),
@@ -561,25 +760,25 @@
 };
 
 static const struct snd_kcontrol_new left_output_mixer[] = {
-SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0),
-SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0),
-SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0),
-SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0),
-SOC_DAPM_SINGLE("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0),
-SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0),
-SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0),
-SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0),
+WM_HUBS_SINGLE_W("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0),
+WM_HUBS_SINGLE_W("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0),
+WM_HUBS_SINGLE_W("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0),
+WM_HUBS_SINGLE_W("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0),
+WM_HUBS_SINGLE_W("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0),
+WM_HUBS_SINGLE_W("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0),
+WM_HUBS_SINGLE_W("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0),
+WM_HUBS_SINGLE_W("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0),
 };
 
 static const struct snd_kcontrol_new right_output_mixer[] = {
-SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0),
-SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0),
-SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0),
-SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0),
-SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0),
-SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0),
-SOC_DAPM_SINGLE("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0),
-SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0),
+WM_HUBS_SINGLE_W("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0),
+WM_HUBS_SINGLE_W("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0),
+WM_HUBS_SINGLE_W("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0),
+WM_HUBS_SINGLE_W("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0),
+WM_HUBS_SINGLE_W("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0),
+WM_HUBS_SINGLE_W("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0),
+WM_HUBS_SINGLE_W("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0),
+WM_HUBS_SINGLE_W("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0),
 };
 
 static const struct snd_kcontrol_new earpiece_mixer[] = {
@@ -943,6 +1142,9 @@
 	struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 
+	hubs->codec = codec;
+
+	INIT_LIST_HEAD(&hubs->dcs_cache);
 	init_completion(&hubs->dcs_done);
 
 	snd_soc_dapm_add_routes(dapm, analogue_routes,
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h
index 5705276..a5a09e6 100644
--- a/sound/soc/codecs/wm_hubs.h
+++ b/sound/soc/codecs/wm_hubs.h
@@ -16,6 +16,8 @@
 
 #include <linux/completion.h>
 #include <linux/interrupt.h>
+#include <linux/list.h>
+#include <sound/control.h>
 
 struct snd_soc_codec;
 
@@ -30,9 +32,9 @@
 	int series_startup;
 	int no_series_update;
 
-	bool no_cache_class_w;
-	bool class_w;
-	u16 class_w_dcs;
+	bool no_cache_dac_hp_direct;
+	struct list_head dcs_cache;
+	bool (*check_class_w_digital)(struct snd_soc_codec *);
 
 	bool lineout1_se;
 	bool lineout1n_ena;
@@ -44,6 +46,8 @@
 
 	bool dcs_done_irq;
 	struct completion dcs_done;
+
+	struct snd_soc_codec *codec;
 };
 
 extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *);
@@ -58,5 +62,9 @@
 extern void wm_hubs_vmid_ena(struct snd_soc_codec *codec);
 extern void wm_hubs_set_bias_level(struct snd_soc_codec *codec,
 				   enum snd_soc_bias_level level);
+extern void wm_hubs_update_class_w(struct snd_soc_codec *codec);
+
+extern const struct snd_kcontrol_new wm_hubs_hpl_mux;
+extern const struct snd_kcontrol_new wm_hubs_hpr_mux;
 
 #endif
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 1a71d2e..ac941f0 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -85,9 +85,26 @@
 	help
 		Say Y if you want to add support for SoC audio on the SMDKs.
 
+config SND_SOC_SAMSUNG_MANTA_WM1811
+	tristate "SoC I2S Audio support for WM1811 on MANTA"
+	depends on SND_SOC_SAMSUNG && MACH_MANTA
+	depends on I2C=y && GENERIC_HARDIRQS
+	select MFD_WM8994
+	select SND_SOC_WM8994
+	select SND_SAMSUNG_I2S
+	help
+	  This adds support for the WM1811 codec on manta.
+
 # For support ALP audio
 source "sound/soc/samsung/srp_alp/Kconfig"
 
+config SND_SOC_SAMSUNG_MANTA_SPDIF
+	tristate "SoC S/PDIF Audio support for MANTA"
+	depends on SND_SOC_SAMSUNG && MACH_MANTA
+	select SND_SAMSUNG_SPDIF
+	help
+	  This adds support spdif for manta.
+
 config SND_SOC_SAMSUNG_SMDK2443_WM9710
 	tristate "SoC AC97 Audio support for SMDK2443 - WM9710"
 	depends on SND_SOC_SAMSUNG && MACH_SMDK2443
@@ -202,6 +219,16 @@
 	help
 	  Say Y if you want to add support for SoC audio on the SMDK
 
+config SND_SOC_MANTA_WM1811_PCM
+	tristate "SoC PCM Audio support for WM1811 on MANTA"
+	depends on SND_SOC_SAMSUNG && MACH_MANTA
+	depends on I2C=y && GENERIC_HARDIRQS
+	select MFD_WM8994
+	select SND_SOC_WM8994
+	select SND_SAMSUNG_PCM
+	help
+	  Say Y if you want to add support for SoC audio on manta.
+
 config SND_SOC_SPEYSIDE
 	tristate "Audio support for Wolfson Speyside"
 	depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile
index 29e9aab..0110871 100644
--- a/sound/soc/samsung/Makefile
+++ b/sound/soc/samsung/Makefile
@@ -43,6 +43,8 @@
 snd-soc-tobermory-objs := tobermory.o
 snd-soc-lowland-objs := lowland.o
 snd-soc-littlemill-objs := littlemill.o
+snd-soc-manta-wm1811-objs := manta_wm1811.o
+snd-soc-manta-spdif-objs := manta_spdif.o
 
 obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -66,3 +68,5 @@
 obj-$(CONFIG_SND_SOC_TOBERMORY) += snd-soc-tobermory.o
 obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o
 obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o
+obj-$(CONFIG_SND_SOC_SAMSUNG_MANTA_WM1811) += snd-soc-manta-wm1811.o
+obj-$(CONFIG_SND_SOC_SAMSUNG_MANTA_SPDIF) += snd-soc-manta-spdif.o
diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c
index e741685..34a5efc 100644
--- a/sound/soc/samsung/littlemill.c
+++ b/sound/soc/samsung/littlemill.c
@@ -187,7 +187,7 @@
 		return ret;
 
 	/* This will check device compatibility itself */
-	wm8958_mic_detect(codec, &littlemill_headset, NULL, NULL);
+	wm8958_mic_detect(codec, &littlemill_headset, NULL, NULL, NULL, NULL);
 
 	/* As will this */
 	wm8994_mic_detect(codec, &littlemill_headset, 1);
diff --git a/sound/soc/samsung/manta_spdif.c b/sound/soc/samsung/manta_spdif.c
new file mode 100644
index 0000000..03c303a
--- /dev/null
+++ b/sound/soc/samsung/manta_spdif.c
@@ -0,0 +1,164 @@
+/*
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <sound/soc.h>
+
+#include "spdif.h"
+
+struct manta_spdif {
+	struct clk *clk_parent;
+	struct clk *clk_spdif;
+};
+
+static int manta_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 *cpu_dai = rtd->cpu_dai;
+	struct manta_spdif *machine =
+				snd_soc_card_get_drvdata(rtd->codec->card);
+
+	unsigned long pll_out, rclk_rate;
+	int ret;
+
+	switch (params_rate(params)) {
+	case 44100:
+		pll_out = 45158400;
+		break;
+	case 32000:
+	case 48000:
+	case 96000:
+		pll_out = 49152000;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Setting ratio to 512fs helps to use SPDIF with HDMI without
+	 * modify SPDIF ASoC machine driver.
+	 */
+	rclk_rate = params_rate(params) * 512;
+
+	clk_set_rate(machine->clk_parent, pll_out);
+	clk_set_rate(machine->clk_spdif, rclk_rate);
+
+	/* Set SPDIF uses internal source clock */
+	ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK,
+					rclk_rate, SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	return ret;
+}
+
+static struct snd_soc_ops manta_spdif_ops = {
+	.hw_params = manta_hw_params,
+};
+
+static struct snd_soc_dai_link manta_dai = {
+	.name = "SPDIF",
+	.stream_name = "SPDIF PCM Playback",
+	.platform_name = "samsung-audio",
+	.cpu_dai_name = "samsung-spdif",
+	.codec_dai_name = "dit-hifi",
+	.codec_name = "spdif-dit",
+	.ops = &manta_spdif_ops,
+};
+
+static struct snd_soc_card manta = {
+	.name = "Manta-SPDIF",
+	.owner = THIS_MODULE,
+	.dai_link = &manta_dai,
+	.num_links = 1,
+};
+
+static int __devinit snd_manta_probe(struct platform_device *pdev)
+{
+	struct manta_spdif *machine;
+	int ret;
+
+	machine = kzalloc(sizeof(*machine), GFP_KERNEL);
+	if (!machine) {
+		pr_err("%s: Failed to allocate memory\n", __func__);
+		ret = -ENOMEM;
+		goto err_kzalloc;
+	}
+
+	machine->clk_parent = clk_get(NULL, "fout_epll");
+	if (IS_ERR(machine->clk_parent)) {
+		pr_err("%s: failed to get fout_epll\n", __func__);
+		ret = PTR_ERR(machine->clk_parent);
+		goto err_clk_parent_get;
+	}
+
+	machine->clk_spdif = clk_get(NULL, "sclk_spdif");
+	if (IS_ERR(machine->clk_spdif)) {
+		pr_err("%s: failed to get sclk_spdif\n", __func__);
+		ret = PTR_ERR(machine->clk_spdif);
+		goto err_clk_spdif_get;
+	}
+
+	snd_soc_card_set_drvdata(&manta, machine);
+
+	manta.dev = &pdev->dev;
+	ret = snd_soc_register_card(&manta);
+	if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
+		goto err_register_card;
+	}
+
+	return 0;
+
+err_register_card:
+	clk_put(machine->clk_spdif);
+err_clk_spdif_get:
+	clk_put(machine->clk_parent);
+err_clk_parent_get:
+	kfree(machine);
+err_kzalloc:
+	return ret;
+}
+
+static int __devexit snd_manta_remove(struct platform_device *pdev)
+{
+	struct manta_spdif *machine = snd_soc_card_get_drvdata(&manta);
+
+	snd_soc_unregister_card(&manta);
+	clk_put(machine->clk_parent);
+	clk_put(machine->clk_spdif);
+	kfree(machine);
+
+	return 0;
+}
+
+static struct platform_driver snd_manta_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "manta-spdif",
+		.pm = &snd_soc_pm_ops,
+	},
+	.probe = snd_manta_probe,
+	.remove = __devexit_p(snd_manta_remove),
+};
+
+module_platform_driver(snd_manta_driver);
+
+MODULE_DESCRIPTION("ALSA SoC Manta SPDIF");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/manta_wm1811.c b/sound/soc/samsung/manta_wm1811.c
new file mode 100644
index 0000000..8a6c819
--- /dev/null
+++ b/sound/soc/samsung/manta_wm1811.c
@@ -0,0 +1,732 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * Copyright (C) 2012 Wolfson Microelectronics
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/wm8994/registers.h>
+
+#include <plat/adc.h>
+
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../codecs/wm8994.h"
+#include "../../../arch/arm/mach-exynos/board-manta.h"
+
+#define MCLK1_FREQ	24000000
+#define MCLK2_FREQ	32768
+
+#define EAR_ADC_CHANNEL		7
+#define ADC_HEADPHONE_3POLE	0x4BA
+#define ADC_HEADSET_4POLE	0xED8
+/* HEADPHONE: [2] - <3 ohm, [1] - Valid, [0] - Mic Accessory is present */
+#define STATUS_HEADPHONE_3POLE	0x7
+/* HEADSET: [10] - >475 ohm, [1] - Valid, [0] - Mic Accessory is present */
+#define STATUS_HEADSET_4POLE	0x403
+#define ADC_MIC_TEST_NUM	10
+#define ADC_MIC_WAIT_US		10000
+
+struct manta_wm1811 {
+	struct clk *clk;
+	unsigned int pll1_out;
+	unsigned int prev_pll1_out;
+	unsigned int pll2_out;
+	unsigned int prev_pll2_out;
+	struct snd_soc_jack jack;
+	struct s3c_adc_client *adc_client;
+};
+
+static const struct snd_kcontrol_new manta_controls[] = {
+	SOC_DAPM_PIN_SWITCH("HP"),
+	SOC_DAPM_PIN_SWITCH("SPK"),
+};
+
+const struct snd_soc_dapm_widget manta_widgets_lunchbox[] = {
+	SND_SOC_DAPM_HP("HP", NULL),
+	SND_SOC_DAPM_SPK("SPK", NULL),
+
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Main Mic", NULL),
+	SND_SOC_DAPM_MIC("Sub Mic", NULL),
+
+	SND_SOC_DAPM_INPUT("S5P RP"),
+};
+
+const struct snd_soc_dapm_widget manta_widgets[] = {
+	SND_SOC_DAPM_HP("HP", NULL),
+	SND_SOC_DAPM_SPK("SPK", NULL),
+
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Main Mic", NULL),
+	SND_SOC_DAPM_MIC("2nd Mic", NULL),
+	SND_SOC_DAPM_MIC("3rd Mic", NULL),
+
+	SND_SOC_DAPM_INPUT("S5P RP"),
+};
+
+const struct snd_soc_dapm_route manta_paths_lunchbox[] = {
+	{ "HP", NULL, "HPOUT1L" },
+	{ "HP", NULL, "HPOUT1R" },
+
+	{ "SPK", NULL, "SPKOUTLN" },
+	{ "SPK", NULL, "SPKOUTLP" },
+	{ "SPK", NULL, "SPKOUTRN" },
+	{ "SPK", NULL, "SPKOUTRP" },
+
+	{ "IN1LP", NULL, "MICBIAS1" },
+	{ "IN1LN", NULL, "MICBIAS1" },
+	{ "MICBIAS1", NULL, "Main Mic" },
+
+	{ "IN1RP", NULL, "MICBIAS1" },
+	{ "IN1RN", NULL, "MICBIAS1" },
+	{ "MICBIAS1", NULL, "Sub Mic" },
+
+	{ "IN2RP:VXRP", NULL, "MICBIAS2" },
+	{ "MICBIAS2", NULL, "Headset Mic" },
+
+	{ "AIF1DAC1L", NULL, "S5P RP" },
+	{ "AIF1DAC1R", NULL, "S5P RP" },
+};
+
+const struct snd_soc_dapm_route manta_paths[] = {
+	{ "HP", NULL, "HPOUT1L" },
+	{ "HP", NULL, "HPOUT1R" },
+
+	{ "SPK", NULL, "SPKOUTLN" },
+	{ "SPK", NULL, "SPKOUTLP" },
+	{ "SPK", NULL, "SPKOUTRN" },
+	{ "SPK", NULL, "SPKOUTRP" },
+
+	{ "IN1LP", NULL, "MICBIAS1" },
+	{ "IN1LN", NULL, "MICBIAS1" },
+	{ "MICBIAS1", NULL, "3rd Mic" },
+
+	{ "IN1RP", NULL, "MICBIAS2" },
+	{ "IN1RN", NULL, "MICBIAS2" },
+	{ "MICBIAS1", NULL, "Headset Mic" },
+
+	{ "IN2LP:VXRN", NULL, "MICBIAS1" },
+	{ "MICBIAS2", NULL, "2nd Mic" },
+
+	{ "IN2RP:VXRP", NULL, "MICBIAS1" },
+	{ "MICBIAS2", NULL, "Main Mic" },
+
+	{ "AIF1DAC1L", NULL, "S5P RP" },
+	{ "AIF1DAC1R", NULL, "S5P RP" },
+};
+
+static int manta_start_fll1(struct snd_soc_dai *codec_dai,
+						struct manta_wm1811 *machine)
+{
+	int ret;
+
+	if (machine->pll1_out != machine->prev_pll1_out) {
+		/*
+		 * FLL1's frequency needs to be changed. Make sure that we
+		 * have a system clock not derived from the FLL, since we
+		 * cannot change the FLL when the system clock is derived
+		 * from it.
+		 * Set FFL clock to maximum during transition in case AIF2
+		 * is active to ensure SYSCLK > 256 x fs
+		 */
+		ret = snd_soc_dai_set_sysclk(codec_dai,
+		                        WM8994_SYSCLK_MCLK1,
+					MCLK1_FREQ / 2, SND_SOC_CLOCK_IN);
+		if (ret < 0) {
+			dev_err(codec_dai->dev,
+				"Failed to switch away from FLL1: %d\n", ret);
+			return ret;
+		}
+
+		machine->prev_pll1_out = machine->pll1_out;
+	}
+
+	/* Switch the FLL */
+	ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1,
+				WM8994_FLL_SRC_MCLK1, MCLK1_FREQ,
+				machine->pll1_out);
+	if (ret < 0) {
+		dev_err(codec_dai->dev, "Unable to start FLL1\n");
+		return ret;
+	}
+
+	/* Then switch AIF1CLK to it */
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1,
+				machine->pll1_out, SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		dev_err(codec_dai->dev, "Unable to switch to FLL1\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int manta_stop_flls(struct snd_soc_dai *codec_dai,
+						struct manta_wm1811 *machine)
+{
+	int ret;
+
+	/*
+	 * Playback/capture has stopped, so switch to the slower
+	 * MCLK2 for reduced power consumption. hw_params handles
+	 * turning the FLL back on when needed.
+	 * Turn FLL2 off as AIF2 is never used if AIF1 is idle. This is
+	 * necessary so that SYSCLK can switch to 32kHz clock.
+	 */
+	ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL2, 0, 0, 0);
+	if (ret < 0) {
+		dev_err(codec_dai->dev, "Failed to stop FLL2: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK2,
+					MCLK2_FREQ, SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		dev_err(codec_dai->dev, "Failed to switch away from FLL: %d\n",
+									ret);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1,
+					0, 0, 0);
+	if (ret < 0) {
+		dev_err(codec_dai->dev, "Failed to stop FLL1: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int manta_set_bias_level(struct snd_soc_card *card,
+					struct snd_soc_dapm_context *dapm,
+					enum snd_soc_bias_level level)
+{
+	struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+	struct manta_wm1811 *machine =
+				snd_soc_card_get_drvdata(card);
+	int ret = 0;
+
+	if (dapm->dev != codec_dai->dev)
+		return 0;
+
+	if ((level == SND_SOC_BIAS_PREPARE) &&
+			(dapm->bias_level == SND_SOC_BIAS_STANDBY))
+		ret = manta_start_fll1(codec_dai, machine);
+
+	return ret;
+}
+
+static int manta_set_bias_level_post(struct snd_soc_card *card,
+					struct snd_soc_dapm_context *dapm,
+					enum snd_soc_bias_level level)
+{
+	struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+	struct manta_wm1811 *machine =
+				snd_soc_card_get_drvdata(card);
+	int ret = 0;
+
+	if (dapm->dev != codec_dai->dev)
+		return 0;
+
+	if (level == SND_SOC_BIAS_STANDBY)
+		ret = manta_stop_flls(codec_dai, machine);
+
+	dapm->bias_level = level;
+
+	return ret;
+}
+
+static int manta_wm1811_aif1_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 *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct manta_wm1811 *machine =
+				snd_soc_card_get_drvdata(rtd->codec->card);
+	int ret;
+
+	machine->pll1_out = params_rate(params) * 512;
+
+	ret = manta_start_fll1(codec_dai, machine);
+	if (ret < 0) {
+		dev_err(codec_dai->dev, "Unable to start FLL1\n");
+		return ret;
+	}
+
+	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) {
+		dev_err(codec_dai->dev, "Unable to set codec DAIFMT\n");
+		return ret;
+	}
+
+	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) {
+		dev_err(codec_dai->dev, "Unable to set CPU DAIFMT\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops manta_wm1811_aif1_ops = {
+	.hw_params = manta_wm1811_aif1_hw_params,
+};
+
+static int manta_wm1811_aif2_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->codec_dai;
+	struct manta_wm1811 *machine =
+	        snd_soc_card_get_drvdata(rtd->codec->card);
+	int ret;
+	int prate;
+
+	prate = params_rate(params);
+
+	switch (prate) {
+	case 8000:
+	case 16000:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Use 512 multiplier to make sure that SYSCLK > 4096kHz
+	 * when fs is 8kHz */
+	machine->pll2_out = prate * 512;
+
+	if (machine->pll2_out != machine->prev_pll2_out) {
+		/*
+		 * FLL2's frequency needs to be changed. Make sure that we
+		 * have a system clock not derived from the FLL, since we
+		 * cannot change the FLL when the system clock is derived
+		 * from it.
+		 * Set FFL clock to maximum during transition in case AIF1
+		 * is active to ensure SYSCLK > 256 x fs
+		 */
+		ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1,
+		                             MCLK1_FREQ / 2, SND_SOC_CLOCK_IN);
+		if (ret < 0) {
+			dev_err(codec_dai->dev,
+			        "Failed to switch away from FLL2: %d\n", ret);
+			return ret;
+		}
+
+		machine->prev_pll2_out = machine->pll2_out;
+	}
+
+	ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL2, WM8994_FLL_SRC_MCLK1,
+						MCLK1_FREQ, machine->pll2_out);
+	if (ret < 0) {
+		dev_err(codec_dai->dev, "Unable to configure FLL2: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL2,
+	                             machine->pll2_out, SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		dev_err(codec_dai->dev, "Unable to switch to FLL2: %d\n", ret);
+		return ret;
+	}
+
+	/* Set the 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) {
+		dev_err(codec_dai->dev, "%s snd_soc_dai_set_fmt error %d\n",
+		        __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops manta_wm1811_aif2_ops = {
+	.hw_params = manta_wm1811_aif2_hw_params,
+};
+
+static struct snd_soc_dai_link manta_dai[] = {
+	{
+		.name = "media-pri",
+		.stream_name = "Media primary",
+		.cpu_dai_name = "samsung-i2s.0",
+		.codec_dai_name = "wm8994-aif1",
+		.platform_name = "samsung-audio",
+		.codec_name = "wm8994-codec",
+		.ops = &manta_wm1811_aif1_ops,
+	},
+	{
+		.name = "media-sec",
+		.stream_name = "Media secondary",
+		.cpu_dai_name = "samsung-i2s.4",
+		.codec_dai_name = "wm8994-aif1",
+#ifdef CONFIG_SND_SAMSUNG_USE_IDMA
+		.platform_name = "samsung-idma",
+#else
+		.platform_name = "samsung-audio",
+#endif
+		.codec_name = "wm8994-codec",
+		.ops = &manta_wm1811_aif1_ops,
+	},
+	{
+		.name = "voice",
+		.stream_name = "Voice",
+		.cpu_dai_name = "manta-voice",
+		.codec_dai_name = "wm8994-aif2",
+		.platform_name = "snd-soc-dummy",
+		.codec_name = "wm8994-codec",
+		.ops = &manta_wm1811_aif2_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = "bt",
+		.stream_name = "Bluetooth",
+		.cpu_dai_name = "manta-bt",
+		.codec_dai_name = "wm8994-aif3",
+		.platform_name = "snd-soc-dummy",
+		.codec_name = "wm8994-codec",
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_driver manta_ext_dai[] = {
+	{
+		.name = "manta-voice",
+		.playback = {
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 16000,
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.capture = {
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 16000,
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+	},
+	{
+		.name = "manta-bt",
+		.playback = {
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 16000,
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.capture = {
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 16000,
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+	},
+};
+
+static struct platform_device android_device_ear = {
+	.name = "android-ear",
+	.id = -1,
+};
+
+static void manta_mic_id(void *data, u16 status)
+{
+	struct snd_soc_codec *codec = data;
+	struct manta_wm1811 *machine =
+				snd_soc_card_get_drvdata(codec->card);
+	int sum = -1;
+	int count = 0;
+	int ret;
+	int i;
+
+	status = STATUS_HEADPHONE_3POLE;
+	if (machine->adc_client) {
+		for (i = 0; i < ADC_MIC_TEST_NUM; i++) {
+			usleep_range(ADC_MIC_WAIT_US, ADC_MIC_WAIT_US);
+			ret = s3c_adc_read(machine->adc_client,
+					   EAR_ADC_CHANNEL);
+			if (ret >= 0) {
+				sum += ret;
+				count++;
+			}
+		}
+		if (count > 0)
+			sum = (sum + 1) / count;
+	}
+	if (sum < 0)
+		pr_err("Error reading ADC line\n");
+	else if (sum > ADC_HEADPHONE_3POLE && sum <= ADC_HEADSET_4POLE)
+		status = STATUS_HEADSET_4POLE;
+
+	wm8958_mic_id(data, status);
+}
+
+static int manta_late_probe(struct snd_soc_card *card)
+{
+	struct snd_soc_codec *codec = card->rtd[0].codec;
+	struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+	struct snd_soc_dai *cpu_dai = card->rtd[0].cpu_dai;
+	struct manta_wm1811 *machine =
+				snd_soc_card_get_drvdata(codec->card);
+	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	/*
+	 * Hack: permit the codec to open streams with the same number
+	 * of channels that the CPU DAI (samsung-i2s) supports, since
+	 * the HDMI block takes its audio from the i2s0 channel shared
+	 * with the codec.
+	 */
+	codec_dai->driver->playback.channels_max =
+			cpu_dai->driver->playback.channels_max;
+
+	/*
+	 * Hack: For using DCS cache from wm1811
+	 * because current wm1811 driver does not use cached value
+	 * and it increases audio warmup time for headphone routing.
+	 * it can help decreasing warmup time
+	 */
+	wm8994->hubs.no_cache_dac_hp_direct = false;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK2,
+				MCLK2_FREQ, SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		dev_err(codec->dev, "Unable to switch to MCLK2\n");
+
+	/* Force AIF1CLK on as it will be master for jack detection */
+	ret = snd_soc_dapm_force_enable_pin(&codec->dapm, "AIF1CLK");
+	if (ret < 0)
+		dev_err(codec->dev, "Failed to enable AIF1CLK\n");
+
+	ret = snd_soc_dapm_disable_pin(&codec->dapm, "S5P RP");
+	if (ret < 0)
+		dev_err(codec->dev, "Failed to disable S5P RP\n");
+
+	ret = snd_soc_jack_new(codec, "Headset",
+				SND_JACK_HEADSET | SND_JACK_MECHANICAL |
+				SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+				SND_JACK_BTN_2, &machine->jack);
+	if (ret) {
+		dev_err(codec->dev, "Failed to create jack: %d\n", ret);
+		return ret;
+	}
+
+	/*
+	 * Settings provided by Wolfson for Samsung-specific customization
+	 * of MICBIAS levels
+	 */
+	snd_soc_write(codec, 0x102, 0x3);
+	snd_soc_write(codec, 0xcb, 0x5151);
+	snd_soc_write(codec, 0xd3, 0x3f3f);
+	snd_soc_write(codec, 0xd4, 0x3f3f);
+	snd_soc_write(codec, 0xd5, 0x3f3f);
+	snd_soc_write(codec, 0xd6, 0x3226);
+	snd_soc_write(codec, 0x102, 0x0);
+	snd_soc_write(codec, 0xd1, 0x87);
+	snd_soc_write(codec, 0x3b, 0x9);
+	snd_soc_write(codec, 0x3c, 0x2);
+
+	ret = snd_jack_set_key(machine->jack.jack, SND_JACK_BTN_0,
+							KEY_MEDIA);
+	if (ret < 0)
+		dev_err(codec->dev, "Failed to set KEY_MEDIA: %d\n", ret);
+
+	ret = snd_jack_set_key(machine->jack.jack, SND_JACK_BTN_1,
+							KEY_VOLUMEUP);
+	if (ret < 0)
+		dev_err(codec->dev, "Failed to set KEY_VOLUMEUP: %d\n", ret);
+
+	ret = snd_jack_set_key(machine->jack.jack, SND_JACK_BTN_2,
+							KEY_VOLUMEDOWN);
+	if (ret < 0)
+		dev_err(codec->dev, "Failed to set KEY_VOLUMEDOWN: %d\n", ret);
+
+	/* certain manta revisions must use SoC ADC mic detection */
+	if (exynos5_manta_get_revision() >= MANTA_REV_DOGFOOD05) {
+		machine->adc_client =
+			s3c_adc_register(&android_device_ear, NULL, NULL, 0);
+		if (IS_ERR(machine->adc_client)) {
+			dev_err(codec->dev, "Failed to set ADC client: %ld\n",
+				PTR_ERR(machine->adc_client));
+			machine->adc_client = NULL;
+		}
+		wm8958_mic_detect(codec, &machine->jack,
+					NULL, NULL, manta_mic_id, codec);
+	} else {
+		wm8958_mic_detect(codec, &machine->jack,
+					NULL, NULL, NULL, NULL);
+	}
+
+	return 0;
+}
+
+static int manta_card_suspend_post(struct snd_soc_card *card)
+{
+	struct snd_soc_codec *codec = card->rtd->codec;
+	struct manta_wm1811 *machine =
+				snd_soc_card_get_drvdata(codec->card);
+
+	snd_soc_update_bits(codec, WM8994_AIF1_MASTER_SLAVE,
+					WM8994_AIF1_TRI_MASK, WM8994_AIF1_TRI);
+
+	clk_disable(machine->clk);
+
+	return 0;
+}
+
+static int manta_card_resume_pre(struct snd_soc_card *card)
+{
+	struct snd_soc_codec *codec = card->rtd->codec;
+	struct manta_wm1811 *machine =
+				snd_soc_card_get_drvdata(codec->card);
+
+	clk_enable(machine->clk);
+
+	snd_soc_update_bits(codec, WM8994_AIF1_MASTER_SLAVE,
+					WM8994_AIF1_TRI_MASK, 0);
+
+	return 0;
+}
+
+static struct snd_soc_card manta = {
+	.name = "Manta-I2S",
+	.owner = THIS_MODULE,
+	.dai_link = manta_dai,
+	.num_links = ARRAY_SIZE(manta_dai),
+
+	.set_bias_level = manta_set_bias_level,
+	.set_bias_level_post = manta_set_bias_level_post,
+
+	.controls = manta_controls,
+	.num_controls = ARRAY_SIZE(manta_controls),
+	.dapm_widgets = manta_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(manta_widgets),
+	.dapm_routes = manta_paths,
+	.num_dapm_routes = ARRAY_SIZE(manta_paths),
+
+	.late_probe = manta_late_probe,
+
+	.suspend_post = manta_card_suspend_post,
+	.resume_pre = manta_card_resume_pre,
+};
+
+static int __devinit snd_manta_probe(struct platform_device *pdev)
+{
+	struct manta_wm1811 *machine;
+	int ret;
+	int hwrev = exynos5_manta_get_revision();
+
+	machine = kzalloc(sizeof(*machine), GFP_KERNEL);
+	if (!machine) {
+		pr_err("Failed to allocate memory\n");
+		ret = -ENOMEM;
+		goto err_kzalloc;
+	}
+
+	machine->clk = clk_get(&pdev->dev, "system_clk");
+	if (IS_ERR(machine->clk)) {
+		pr_err("failed to get system_clk\n");
+		ret = PTR_ERR(machine->clk);
+		goto err_clk_get;
+	}
+
+	/* Start the reference clock for the codec's FLL */
+	clk_enable(machine->clk);
+
+	machine->pll1_out = 44100 * 512; /* default sample rate */
+	machine->pll2_out = 0;
+
+	ret = snd_soc_register_dais(&pdev->dev, manta_ext_dai,
+						ARRAY_SIZE(manta_ext_dai));
+	if (ret != 0)
+		pr_err("Failed to register external DAIs: %d\n", ret);
+
+	snd_soc_card_set_drvdata(&manta, machine);
+
+	if (hwrev < MANTA_REV_PRE_ALPHA) {
+		manta.dapm_widgets = manta_widgets_lunchbox,
+		manta.num_dapm_widgets = ARRAY_SIZE(manta_widgets_lunchbox),
+		manta.dapm_routes = manta_paths_lunchbox;
+		manta.num_dapm_routes = ARRAY_SIZE(manta_paths_lunchbox);
+	}
+
+	manta.dev = &pdev->dev;
+	ret = snd_soc_register_card(&manta);
+	if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
+		goto err_register_card;
+	}
+
+	return 0;
+
+err_register_card:
+	clk_put(machine->clk);
+err_clk_get:
+	kfree(machine);
+err_kzalloc:
+	return ret;
+}
+
+static int __devexit snd_manta_remove(struct platform_device *pdev)
+{
+	struct manta_wm1811 *machine = snd_soc_card_get_drvdata(&manta);
+
+	if (machine->adc_client)
+		s3c_adc_release(machine->adc_client);
+	snd_soc_unregister_card(&manta);
+	clk_disable(machine->clk);
+	clk_put(machine->clk);
+	kfree(machine);
+
+	return 0;
+}
+
+static struct platform_driver snd_manta_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "manta-i2s",
+		.pm = &snd_soc_pm_ops,
+	},
+	.probe = snd_manta_probe,
+	.remove = __devexit_p(snd_manta_remove),
+};
+
+module_platform_driver(snd_manta_driver);
+
+MODULE_DESCRIPTION("ALSA SoC Manta WM1811");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 0ad8dca..20cee6f 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -351,13 +351,15 @@
 	/* Muting the DAC suppresses artifacts caused during digital
 	 * shutdown, for example from stopping clocks.
 	 */
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK
+				&& !codec_dai->playback_active)
 		snd_soc_dai_digital_mute(codec_dai, 1);
 
 	if (cpu_dai->driver->ops->shutdown)
 		cpu_dai->driver->ops->shutdown(substream, cpu_dai);
 
-	if (codec_dai->driver->ops->shutdown)
+	if (codec_dai->driver->ops->shutdown
+				&& !codec_dai->playback_active)
 		codec_dai->driver->ops->shutdown(substream, codec_dai);
 
 	if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
@@ -367,24 +369,26 @@
 		platform->driver->ops->close(substream);
 	cpu_dai->runtime = NULL;
 
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		if (!rtd->pmdown_time || codec->ignore_pmdown_time ||
-		    rtd->dai_link->ignore_pmdown_time) {
-			/* powered down playback stream now */
-			snd_soc_dapm_stream_event(rtd,
+	if (!codec_dai->playback_active) {
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			if (!rtd->pmdown_time || codec->ignore_pmdown_time ||
+			    rtd->dai_link->ignore_pmdown_time) {
+				/* powered down playback stream now */
+				snd_soc_dapm_stream_event(rtd,
 						  SNDRV_PCM_STREAM_PLAYBACK,
 						  codec_dai,
 						  SND_SOC_DAPM_STREAM_STOP);
+			} else {
+				/* start delayed pop wq here for playback streams */
+				codec_dai->pop_wait = 1;
+				schedule_delayed_work(&rtd->delayed_work,
+					msecs_to_jiffies(rtd->pmdown_time));
+			}
 		} else {
-			/* start delayed pop wq here for playback streams */
-			codec_dai->pop_wait = 1;
-			schedule_delayed_work(&rtd->delayed_work,
-				msecs_to_jiffies(rtd->pmdown_time));
-		}
-	} else {
-		/* capture streams can be powered down now */
-		snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE,
+			/* capture streams can be powered down now */
+			snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE,
 					  codec_dai, SND_SOC_DAPM_STREAM_STOP);
+		}
 	}
 
 	mutex_unlock(&rtd->pcm_mutex);